diff --git a/.github/workflows/wine-fedora.yml b/.github/workflows/wine-fedora.yml index 6c09d9722..2e8ce2cf1 100644 --- a/.github/workflows/wine-fedora.yml +++ b/.github/workflows/wine-fedora.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: Compilation run: | - sudo dnf -y -q update + sudo dnf -y -q upgrade --refresh cd wine-tkg-git sed -i 's/distro=""/distro="fedora"/' customization.cfg touch tarplz diff --git a/.github/workflows/wine-ubuntu.yml b/.github/workflows/wine-ubuntu.yml index 016a26bce..ac7feff53 100644 --- a/.github/workflows/wine-ubuntu.yml +++ b/.github/workflows/wine-ubuntu.yml @@ -18,6 +18,7 @@ jobs: sudo aptitude remove -y '?narrow(?installed,?version(deb.sury.org))' cd wine-tkg-git sed -i 's/distro=""/distro="debuntu"/' customization.cfg + sed -i 's/_NOLIB32="false"/_NOLIB32="wow64"/' wine-tkg-profiles/advanced-customization.cfg echo '_ci_build="true"' >> customization.cfg touch tarplz yes|./non-makepkg-build.sh diff --git a/.github/workflows/wine-valvexbe.yml b/.github/workflows/wine-valvexbe.yml index aeb5467ce..3f02a5997 100644 --- a/.github/workflows/wine-valvexbe.yml +++ b/.github/workflows/wine-valvexbe.yml @@ -18,6 +18,7 @@ jobs: cd wine-tkg-git sed -i 's/distro=""/distro="debuntu"/' customization.cfg sed -i 's/LOCAL_PRESET=""/LOCAL_PRESET="valve-exp-bleeding"/' customization.cfg + sed -i 's/_NOLIB32="false"/_NOLIB32="wow64"/' wine-tkg-profiles/advanced-customization.cfg echo '_ci_build="true"' >> customization.cfg touch tarplz yes|./non-makepkg-build.sh diff --git a/proton-tkg/proton-tkg-profiles/advanced-customization.cfg b/proton-tkg/proton-tkg-profiles/advanced-customization.cfg index f43fb81f1..9b218a049 100644 --- a/proton-tkg/proton-tkg-profiles/advanced-customization.cfg +++ b/proton-tkg/proton-tkg-profiles/advanced-customization.cfg @@ -9,7 +9,7 @@ # Some options will be missing from this config file compared to wine-tkg-git as they are enforced. # Proton branch to target for lsteamclient libs and steam helper on 4.x+ - When using a Wine 3.x base, "proton_3.16" branch will be enforced -_proton_branch="experimental_7.0" +_proton_branch="experimental_8.0" # Set to "true" to use the "noruntime" proton toolmanifest if available # Set to "sniper" to use the experimental sniper runtime diff --git a/proton-tkg/proton-tkg-profiles/sample-external-config.cfg b/proton-tkg/proton-tkg-profiles/sample-external-config.cfg index d3d75c364..a406fadc9 100644 --- a/proton-tkg/proton-tkg-profiles/sample-external-config.cfg +++ b/proton-tkg/proton-tkg-profiles/sample-external-config.cfg @@ -28,7 +28,7 @@ _no_autoinstall="false" # PROTON-TKG OPTIONS # Proton branch to target for lsteamclient libs and steam helper on 4.x+ - When using a Wine 3.x base, "proton_3.16" branch will be enforced -_proton_branch="experimental_7.0" +_proton_branch="experimental_8.0" # Proton SDL Joystick support, xinput hacks and other gamepad additions. _gamepad_additions depends on _sdl_joy_support. # This might be required for some FFB steering wheels, but can break gamepad support in some games. diff --git a/proton-tkg/proton-tkg.sh b/proton-tkg/proton-tkg.sh index 54ed4a785..e0ebc2d13 100755 --- a/proton-tkg/proton-tkg.sh +++ b/proton-tkg/proton-tkg.sh @@ -170,7 +170,7 @@ function build_vrclient { export CFLAGS="-O2 -g" export CXXFLAGS="-Wno-attributes -std=c++0x -O2 -g" PATH="$_nowhere"/proton_dist_tmp/bin:$PATH - if [[ "$_proton_branch" = *6.* ]] || [[ "$_proton_branch" = *7.* ]]; then + if [[ "$_proton_branch" = *6.* ]] || [[ "$_proton_branch" = *7.* ]] || [[ "$_proton_branch" = *8.* ]]; then WINEMAKERFLAGS+=" -ldl" elif [ "$_standard_dlopen" = "true" ] && [[ "$_proton_branch" != *5.13 ]]; then patch -Np1 < "$_nowhere/proton_template/vrclient-remove-library.h-dep.patch" || exit 1 @@ -187,14 +187,23 @@ function build_vrclient { rm -rf build/vrclient.win32 mkdir -p build/vrclient.win64 mkdir -p build/vrclient.win32 + if [ ! -e "$_nowhere/proton_dist_tmp/include/wine/unixlib.h" ]; then + cp "$_wine_tkg_git_path/src/$_winesrcdir/include/wine/unixlib.h" "$_nowhere/proton_dist_tmp/include/wine/" + fi cp -a "${_nowhere}"/Proton/vrclient_x64/* build/vrclient.win64 - cp -a "${_nowhere}"/Proton/vrclient_x64/* build/vrclient.win32 && mv build/vrclient.win32/vrclient_x64 build/vrclient.win32/vrclient && mv build/vrclient.win32/vrclient/vrclient_x64.spec build/vrclient.win32/vrclient/vrclient.spec + cp -a "${_nowhere}"/Proton/vrclient_x64/* build/vrclient.win32 + mv build/vrclient.win32/vrclient_x64 build/vrclient.win32/vrclient + if [ -e build/vrclient.win32/vrclient/vrclient_x64.spec ]; then + mv build/vrclient.win32/vrclient/vrclient_x64.spec build/vrclient.win32/vrclient/vrclient.spec + _vrclient_longpath64="/vrclient_x64" + _vrclient_longpath32="/vrclient" + fi cd build/vrclient.win64 winemaker $WINEMAKERFLAGS -L"$_nowhere/proton_dist_tmp/$_lib64name/" -L"$_nowhere/proton_dist_tmp/$_lib64name/wine/" -I"$_nowhere/openvr/build/vrclient.win64/vrclient_x64/" -I"$_nowhere/openvr/build/vrclient.win64/" vrclient_x64 make -e CC="winegcc -m64" CXX="wineg++ -m64 $_cxx_addon" -C "$_nowhere/openvr/build/vrclient.win64/vrclient_x64" -j$(nproc) && strip --strip-debug vrclient_x64/vrclient_x64.dll.so || exit 1 - winebuild --dll --fake-module -E "$_nowhere/openvr/build/vrclient.win64/vrclient_x64/vrclient_x64.spec" -o vrclient_x64.dll.fake || exit 1 + winebuild --dll --fake-module -E "$_nowhere/openvr/build/vrclient.win64$_vrclient_longpath64/vrclient_x64.spec" -o vrclient_x64.dll.fake || exit 1 cd ../.. cd build/vrclient.win32 @@ -202,7 +211,7 @@ function build_vrclient { winemaker $WINEMAKERFLAGS --wine32 -L"$_nowhere/proton_dist_tmp/$_lib32name/" -L"$_nowhere/proton_dist_tmp/$_lib32name/wine/" -I"$_nowhere/openvr/build/vrclient.win32/vrclient/" -I"$_nowhere/openvr/build/vrclient.win32/" vrclient make -e CC="winegcc -m32" CXX="wineg++ -m32 $_cxx_addon" -C "$_nowhere/openvr/build/vrclient.win32/vrclient" -j$(nproc) && strip --strip-debug vrclient/vrclient.dll.so || exit 1 fi - winebuild --dll --fake-module -E "$_nowhere/openvr/build/vrclient.win32/vrclient/vrclient.spec" -o vrclient.dll.fake || exit 1 + winebuild --dll --fake-module -E "$_nowhere/openvr/build/vrclient.win32$_vrclient_longpath32/vrclient.spec" -o vrclient.dll.fake || exit 1 cd "$_nowhere" mkdir -p proton_dist_tmp/lib/wine/dxvk @@ -395,6 +404,8 @@ function build_mediaconverter { fi if [ "$_build_mediaconv" = "true" ]; then + # git 2.40 workaround + git config --local index.skipHash false && git add . if [ -d "$_nowhere"/Proton/media-converter ]; then cd "$_nowhere"/Proton/media-converter @@ -436,7 +447,7 @@ function build_mediaconverter { function build_steamhelper { export CFLAGS="-Wno-attributes -O2 -g" - export CXXFLAGS="-Wno-attributes -O2 -g" + export CXXFLAGS="-Wno-attributes -O2 -g -fpermissive" # disable openvr support for now since we don't support it if [[ "$_proton_branch" = *6.3 ]]; then _cxx_addon="-std=c++17" @@ -457,7 +468,7 @@ function build_steamhelper { fi fi - if [ "$_processinfoclass" = "true" ]; then + if [[ "$_proton_branch" = *6.* ]] || [[ "$_proton_branch" = *7.* ]] && [ "$_processinfoclass" = "true" ]; then ( cd Proton && patch -Np1 < "$_nowhere/proton_template/steamhelper_PROCESSINFOCLASS.patch" ) || exit 1 fi @@ -487,7 +498,7 @@ function build_steamhelper { if [ "$_NOLIB32" != "true" ]; then # 32-bit if [ -e "$_nowhere"/Proton/steam_helper/32/libsteam_api.so ]; then - make -e CC="winegcc -m32" CXX="wineg++ -m32 $_cxx_addon" -C "$_nowhere/Proton/build/steam.win32" LIBRARIES="-L$_nowhere/Proton/steam_helper/32/ -lsteam_api -lole32 -lmsi -ldl -static-libgcc -static-libstdc++" -j$(nproc) && strip --strip-debug steam.exe.so || exit 1 + make -e CC="winegcc -m32" CXX="wineg++ -m32 $_cxx_addon" -C "$_nowhere/Proton/build/steam.win32" LIBRARIES="-L$_nowhere/Proton/steam_helper/32/ -lsteam_api -lole32 -lshlwapi -lmsi -ldl -static-libgcc -static-libstdc++" -j$(nproc) && strip --strip-debug steam.exe.so || exit 1 else make -e CC="winegcc -m32" CXX="wineg++ -m32 $_cxx_addon" -C "$_nowhere/Proton/build/steam.win32" LIBRARIES="-lsteam_api -lole32 -ldl -static-libgcc -static-libstdc++" -j$(nproc) && strip --strip-debug steam.exe.so || exit 1 fi @@ -502,7 +513,7 @@ function build_steamhelper { if [ -e "$_nowhere"/Proton/steam_helper/64/libsteam_api.so ]; then cd "$_nowhere"/Proton/build/steam.win64 winemaker $WINEMAKERFLAGS --guiexe -lsteam_api -lole32 -I"$_nowhere/Proton/lsteamclient/steamworks_sdk_142/" -I"$_nowhere/openvr/headers/" -L"$_nowhere/Proton/steam_helper" . - make -e CC="winegcc -m64" CXX="wineg++ -m64 $_cxx_addon" -C "$_nowhere/Proton/build/steam.win64" LIBRARIES="-L$_nowhere/Proton/steam_helper/64/ -lsteam_api -lmsi -lole32 -ldl -static-libgcc -static-libstdc++" -j$(nproc) && strip --strip-debug steam.exe.so + make -e CC="winegcc -m64" CXX="wineg++ -m64 $_cxx_addon" -C "$_nowhere/Proton/build/steam.win64" LIBRARIES="-L$_nowhere/Proton/steam_helper/64/ -lsteam_api -lshlwapi -lmsi -lole32 -ldl -static-libgcc -static-libstdc++" -j$(nproc) && strip --strip-debug steam.exe.so touch "$_nowhere/Proton/build/steam.win64/steam.spec" @@ -718,8 +729,9 @@ function download_dxvk_version { echo "#######################################################" echo "" echo "$_dxvk_version_response" \ - | grep "browser_download_url.*tar.gz" \ - | cut -d : -f 2,3 \ + | jq .assets[].browser_download_url \ + | grep -v "dxvk-native" \ + | head -1 \ | tr -d \" \ | wget -qi - break @@ -929,11 +941,16 @@ else cd Proton fi + # Tooling compilation needs an update for latest BE - Use slightly older tooling for now + if [ -n "$_bleeding_tag" ] || [[ "$_proton_branch" = experimental_8* ]]; then + git checkout f5e9c76903e4e18e0416e719a6d42d0cb00998aa + fi + # Embed fake data to spoof desired fonts - fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSans-Regular" "Arial" "Arial" "Arial" - fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSans-Bold" "Arial-Bold" "Arial" "Arial Bold" - fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSerif-Regular" "TimesNewRoman" "Times New Roman" "Times New Roman" - fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationMono-Regular" "CourierNew" "Courier New" "Courier New" + fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSans-Regular" "Arial" "Arial" "Arial" "$_nowhere/proton_template/share/fonts"/arial.ttf + fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSans-Bold" "Arial-Bold" "Arial" "Arial Bold" "$_nowhere/proton_template/share/fonts"/arialbd.ttf + fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationSerif-Regular" "TimesNewRoman" "Times New Roman" "Times New Roman" "$_nowhere/proton_template/share/fonts"/times.ttf + fontforge -script "$_nowhere/Proton/fonts/scripts/generatefont.pe" "$_nowhere/proton_template/share/fonts/LiberationMono-Regular" "CourierNew" "Courier New" "Courier New" "$_nowhere/proton_template/share/fonts"/cour.ttf # Build GST/mediaconverter if [ "$_build_mediaconv" = "true" ] || [ "$_build_gstreamer" = "true" ]; then diff --git a/proton-tkg/proton_template/conf/proton b/proton-tkg/proton_template/conf/proton index cd2d60c3e..55fadb70d 100755 --- a/proton-tkg/proton_template/conf/proton +++ b/proton-tkg/proton_template/conf/proton @@ -1134,13 +1134,21 @@ class CompatData: if not os.path.exists(self.prefix_dir + "drive_c/windows/syswow64"): try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12.dll", self.prefix_dir + "drive_c/windows/system32/d3d12.dll") + try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12core.dll", + self.prefix_dir + "drive_c/windows/system32/d3d12core.dll") g_session.dlloverrides["d3d12"] = "n" + g_session.dlloverrides["d3d12core"] = "n" else: try_copy(g_proton.lib64_dir + "wine/vkd3d-proton/d3d12.dll", self.prefix_dir + "drive_c/windows/system32/d3d12.dll") try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12.dll", self.prefix_dir + "drive_c/windows/syswow64/d3d12.dll") + try_copy(g_proton.lib64_dir + "wine/vkd3d-proton/d3d12core.dll", + self.prefix_dir + "drive_c/windows/system32/d3d12core.dll") + try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12core.dll", + self.prefix_dir + "drive_c/windows/syswow64/d3d12core.dll") g_session.dlloverrides["d3d12"] = "n" + g_session.dlloverrides["d3d12core"] = "n" setup_game_dir_drive() setup_steam_dir_drive() diff --git a/proton-tkg/proton_template/conf/user_settings.py b/proton-tkg/proton_template/conf/user_settings.py index f54939e92..314f4fd2b 100755 --- a/proton-tkg/proton_template/conf/user_settings.py +++ b/proton-tkg/proton_template/conf/user_settings.py @@ -37,7 +37,7 @@ "PROTON_NVAPI_DISABLE": "1", #Pass "--use-gl=osmesa" to the command line. Fixes various launchers showing up with a black window (Star Citizen, Warframe, etc..). Might give poor perf on native opengl games, so disable in case of issues. - "PROTON_GL_OSMESA": "1", +# "PROTON_GL_OSMESA": "1", #Set a wine override to disable libglesv2. Fixes various launchers showing up with a black window (Star Citizen, Warframe, etc..). Alternative to running an app with `--use-gl=osmesa`, but more extreme. #Known to break at least the Rockstar Games Launcher, so only use if the PROTON_GL_OSMESA option above doesn't fix your issue. diff --git a/proton-tkg/proton_template/ffmpeg_binutils.patch b/proton-tkg/proton_template/ffmpeg_binutils.patch new file mode 100644 index 000000000..305f19ce9 --- /dev/null +++ b/proton-tkg/proton_template/ffmpeg_binutils.patch @@ -0,0 +1,74 @@ +From: RĂ©mi Denis-Courmont +Date: Sun, 16 Jul 2023 15:18:02 +0000 (+0300) +Subject: avcodec/x86/mathops: clip constants used with shift instructions within inline assembly +X-Git-Tag: n6.1~1007 +X-Git-Url: http://git.ffmpeg.org/gitweb/ffmpeg.git/commitdiff_plain/effadce6c756247ea8bae32dc13bb3e6f464f0eb + +avcodec/x86/mathops: clip constants used with shift instructions within inline assembly + +Fixes assembling with binutil as >= 2.41 + +Signed-off-by: James Almer +--- + +diff --git a/libavcodec/x86/mathops.h b/libavcodec/x86/mathops.h +index 6298f5ed19..ca7e2dffc1 100644 +--- a/libavcodec/x86/mathops.h ++++ b/libavcodec/x86/mathops.h +@@ -35,12 +35,20 @@ + static av_always_inline av_const int MULL(int a, int b, unsigned shift) + { + int rt, dummy; ++ if (__builtin_constant_p(shift)) + __asm__ ( + "imull %3 \n\t" + "shrdl %4, %%edx, %%eax \n\t" + :"=a"(rt), "=d"(dummy) +- :"a"(a), "rm"(b), "ci"((uint8_t)shift) ++ :"a"(a), "rm"(b), "i"(shift & 0x1F) + ); ++ else ++ __asm__ ( ++ "imull %3 \n\t" ++ "shrdl %4, %%edx, %%eax \n\t" ++ :"=a"(rt), "=d"(dummy) ++ :"a"(a), "rm"(b), "c"((uint8_t)shift) ++ ); + return rt; + } + +@@ -113,19 +121,31 @@ __asm__ volatile(\ + // avoid +32 for shift optimization (gcc should do that ...) + #define NEG_SSR32 NEG_SSR32 + static inline int32_t NEG_SSR32( int32_t a, int8_t s){ ++ if (__builtin_constant_p(s)) + __asm__ ("sarl %1, %0\n\t" + : "+r" (a) +- : "ic" ((uint8_t)(-s)) ++ : "i" (-s & 0x1F) + ); ++ else ++ __asm__ ("sarl %1, %0\n\t" ++ : "+r" (a) ++ : "c" ((uint8_t)(-s)) ++ ); + return a; + } + + #define NEG_USR32 NEG_USR32 + static inline uint32_t NEG_USR32(uint32_t a, int8_t s){ ++ if (__builtin_constant_p(s)) + __asm__ ("shrl %1, %0\n\t" + : "+r" (a) +- : "ic" ((uint8_t)(-s)) ++ : "i" (-s & 0x1F) + ); ++ else ++ __asm__ ("shrl %1, %0\n\t" ++ : "+r" (a) ++ : "c" ((uint8_t)(-s)) ++ ); + return a; + } + + diff --git a/proton-tkg/proton_template/gstreamer.sh b/proton-tkg/proton_template/gstreamer.sh index 52a4219ce..b4871f59f 100755 --- a/proton-tkg/proton_template/gstreamer.sh +++ b/proton-tkg/proton_template/gstreamer.sh @@ -63,6 +63,7 @@ if [ "$_build_ffmpeg" = "true" ]; then mkdir -p "$_nowhere"/Proton/build/FFmpeg64 && cd "$_nowhere"/Proton/build/FFmpeg64 + ( cd "$_nowhere"/external-resources/FFmpeg && patch -Np1 < "$_nowhere"/proton_template/ffmpeg_binutils.patch ) "$_nowhere"/Proton/FFmpeg/configure \ --prefix="$_nowhere/gst" \ @@ -235,6 +236,7 @@ -D gst-plugins-bad:voamrwbenc=disabled -D gst-plugins-bad:x265=disabled -D gst-plugins-bad:openexr=disabled + -D gst-plugins-bad:lc3=disabled -D gst-plugins-ugly:gobject-cast-checks=disabled -D gst-rtsp-server:gobject-cast-checks=disabled -D gst-editing-services:validate=disabled @@ -373,6 +375,8 @@ -D gst-plugins-good:rpicamsrc=disabled -D gst-plugins-good:aalib=disabled -D gst-plugins-good:alpha=disabled + -D gst-plugins-good:amrnb=disabled + -D gst-plugins-good:amrwbdec=disabled -D gst-plugins-good:apetag=disabled -D gst-plugins-good:audiofx=disabled -D gst-plugins-good:auparse=disabled @@ -474,6 +478,7 @@ -D gst-plugins-bad:soundtouch=disabled -D gst-plugins-bad:svtav1=disabled -D gst-plugins-bad:ladspa=disabled + -D gst-plugins-bad:lc3=disabled -D gst-plugins-bad:ldac=disabled -D gst-plugins-bad:openaptx=disabled -D gst-plugins-bad:microdns=disabled @@ -493,8 +498,6 @@ -D gst-plugins-bad:bs2b=disabled -D gst-plugins-bad:timecode=disabled -D gst-plugins-ugly:gobject-cast-checks=disabled - -D gst-plugins-ugly:amrnb=disabled - -D gst-plugins-ugly:amrwbdec=disabled -D gst-plugins-ugly:cdio=disabled -D gst-plugins-ugly:dvdread=disabled -D gst-rtsp-server:gobject-cast-checks=disabled diff --git a/wine-tkg-git/PKGBUILD b/wine-tkg-git/PKGBUILD index d9ccbb1b2..6aa2efbe8 100644 --- a/wine-tkg-git/PKGBUILD +++ b/wine-tkg-git/PKGBUILD @@ -32,7 +32,9 @@ fi # custom plain wine commit to pass to git if [ -n "$_plain_version" ]; then - if [ "$_use_staging" = "false" ] || ( [ "$_use_staging" = "true" ] && [ "$_staging_upstreamignore" = "true" ] ); then + if [[ "$_LOCAL_PRESET" = valve* ]]; then + _plain_commit="#branch=$_plain_version" + elif [ "$_use_staging" = "false" ] || ( [ "$_use_staging" = "true" ] && [ "$_staging_upstreamignore" = "true" ] ); then _plain_commit="#commit=$_plain_version" fi fi @@ -69,59 +71,73 @@ options=(${_makepkg_options[@]} !lto) license=('LGPL') depends=( - 'attr' 'lib32-attr' - 'fontconfig' 'lib32-fontconfig' - 'lcms2' 'lib32-lcms2' - 'libxml2' 'lib32-libxml2' - 'libxcursor' 'lib32-libxcursor' - 'libxrandr' 'lib32-libxrandr' - 'libxdamage' 'lib32-libxdamage' - 'libxi' 'lib32-libxi' - 'gettext' 'lib32-gettext' - 'freetype2' 'lib32-freetype2' - 'glu' 'lib32-glu' - 'libsm' 'lib32-libsm' - 'gcc-libs' 'lib32-gcc-libs' - 'libpcap' 'lib32-libpcap' - 'desktop-file-utils' 'jxrlib' + 'attr' 'fontconfig' + 'lcms2' 'libxml2' + 'libxcursor' 'libxrandr' + 'libxdamage' 'libxi' + 'gettext' 'freetype2' + 'glu' 'libsm' + 'gcc-libs' 'libpcap' + 'jxrlib' 'desktop-file-utils' $_user_deps ) -makedepends=('git' 'autoconf' 'ncurses' 'bison' 'perl' 'fontforge' 'flex' - 'gcc>=4.5.0-2' 'pkgconf' - 'giflib' 'lib32-giflib' - 'libpng' 'lib32-libpng' - 'gnutls' 'lib32-gnutls' - 'libxinerama' 'lib32-libxinerama' - 'libxcomposite' 'lib32-libxcomposite' - 'libxmu' 'lib32-libxmu' - 'libxxf86vm' 'lib32-libxxf86vm' - 'libldap' 'lib32-libldap' - 'mpg123' 'lib32-mpg123' - 'openal' 'lib32-openal' - 'v4l-utils' 'lib32-v4l-utils' - 'alsa-lib' 'lib32-alsa-lib' - 'libxcomposite' 'lib32-libxcomposite' - 'mesa' 'lib32-mesa' - 'libgl' 'lib32-libgl' - 'libxslt' 'lib32-libxslt' - 'libpulse' 'lib32-libpulse' - 'libva' 'lib32-libva' - 'gtk3' 'lib32-gtk3' - 'gst-plugins-base-libs' 'lib32-gst-plugins-base-libs' - 'gst-plugins-good' 'lib32-gst-plugins-good' - 'vulkan-icd-loader' 'lib32-vulkan-icd-loader' - 'sdl2' 'lib32-sdl2' - 'libcups' 'lib32-libcups' - 'samba' 'opencl-headers' - 'meson' 'ninja' - 'glslang' 'wget' - 'ocl-icd' 'lib32-ocl-icd' - 'jack' 'lib32-jack' - 'libxpresent' 'libgcrypt' - 'lib32-libgcrypt' $_user_makedeps +if [ "$_NOLIB32" = "false" ]; then + depends+=( + 'lib32-attr' 'lib32-fontconfig' + 'lib32-lcms2' 'lib32-lcms2' + 'lib32-libxml2' 'lib32-libxcursor' + 'lib32-libxrandr' 'lib32-libxdamage' + 'lib32-libxi' 'lib32-gettext' + 'lib32-freetype2' 'lib32-glu' + 'lib32-libsm' 'lib32-gcc-libs' + 'lib32-libpcap' + ) +fi + +makedepends=( + 'git' 'autoconf' + 'ncurses' 'bison' + 'perl' 'fontforge' + 'flex' 'gcc>=4.5.0-2' + 'pkgconf' 'giflib' + 'libpng' 'gnutls' + 'libxinerama' 'libxcomposite' + 'libxmu' 'libxxf86vm' + 'libldap' 'mpg123' + 'openal' 'v4l-utils' + 'alsa-lib' 'libxcomposite' + 'mesa' 'libgl' + 'libxslt' 'libpulse' + 'libva' 'gtk3' + 'gst-plugins-base-libs' 'gst-plugins-good' + 'vulkan-icd-loader' 'sdl2' + 'libcups' 'samba' + 'opencl-headers' 'meson' + 'ninja' 'glslang' + 'wget' 'ocl-icd' + 'jack' 'libxpresent' + 'libgcrypt' $_user_makedeps ) +if [ "$_NOLIB32" = "false" ]; then + makedepends+=( + 'lib32-giflib' 'lib32-libpng' + 'lib32-gnutls' 'lib32-libxinerama' + 'lib32-libxcomposite' 'lib32-libxmu' + 'lib32-libxxf86vm' 'lib32-libldap' + 'lib32-mpg123' 'lib32-openal' + 'lib32-v4l-utils' 'lib32-alsa-lib' + 'lib32-mesa' 'lib32-libgl' + 'lib32-libxslt' 'lib32-libpulse' + 'lib32-libva' 'lib32-gtk3' + 'lib32-gst-plugins-base-libs' 'lib32-gst-plugins-good' + 'lib32-vulkan-icd-loader' 'lib32-sdl2' + 'lib32-libcups' 'lib32-ocl-icd' + 'lib32-jack' 'lib32-libgcrypt' + ) +fi + # mingw if [ -z "${CUSTOM_MINGW_PATH}" ]; then makedepends+=('mingw-w64-gcc') @@ -142,33 +158,37 @@ if [ "$_use_mono" = "true" ]; then fi optdepends=( - 'giflib' 'lib32-giflib' - 'libpng' 'lib32-libpng' - 'libldap' 'lib32-libldap' - 'gnutls' 'lib32-gnutls' - 'mpg123' 'lib32-mpg123' - 'openal' 'lib32-openal' - 'v4l-utils' 'lib32-v4l-utils' - 'libpulse' 'lib32-libpulse' - 'alsa-plugins' 'lib32-alsa-plugins' - 'alsa-lib' 'lib32-alsa-lib' - 'libjpeg-turbo' 'lib32-libjpeg-turbo' - 'libxcomposite' 'lib32-libxcomposite' - 'libxinerama' 'lib32-libxinerama' - 'ncurses' 'lib32-ncurses' - 'libxslt' 'lib32-libxslt' - 'libva' 'lib32-libva' - 'gtk3' 'lib32-gtk3' - 'gst-plugins-base-libs' 'lib32-gst-plugins-base-libs' - 'vulkan-icd-loader' 'lib32-vulkan-icd-loader' - 'sdl2' 'lib32-sdl2' - 'cups' 'zapcc' - 'samba' 'clang' - 'dosbox' 'ccache' - 'faudio' 'lib32-faudio' - 'schedtool' + 'giflib' 'libpng' + 'libldap' 'gnutls' + 'mpg123' 'openal' + 'v4l-utils' 'libpulse' + 'alsa-plugins' 'alsa-lib' + 'libjpeg-turbo' 'libxcomposite' + 'libxinerama' 'ncurses' + 'libxslt' 'libva' + 'gtk3' 'gst-plugins-base-libs' + 'vulkan-icd-loader' 'sdl2' + 'cups' 'samba' + 'dosbox' 'faudio' + 'schedtool' 'zapcc' + 'clang' 'ccache' ) +if [ "$_NOLIB32" = "false" ]; then + optdepends+=( + 'lib32-giflib' 'lib32-libpng' + 'lib32-libldap' 'lib32-gnutls' + 'lib32-mpg123' 'lib32-openal' + 'lib32-v4l-utils' 'lib32-libpulse' + 'lib32-alsa-plugins' 'lib32-alsa-lib' + 'lib32-libjpeg-turbo' 'lib32-libxcomposite' + 'lib32-libxinerama' 'lib32-ncurses' + 'lib32-libxslt' 'lib32-libva' + 'lib32-gtk3' 'lib32-gst-plugins-base-libs' + 'lib32-vulkan-icd-loader' 'lib32-sdl2' + 'lib32-faudio' + ) +fi # Wine source if [ "$_github_mirrorsrc" = "true" ]; then @@ -203,7 +223,7 @@ fi # Handle unbranched bleeding version if [[ "$_LOCAL_PRESET" = valve* ]] && [ -n "$_bleeding_tag" ]; then - _plain_commit="${_bleeding_tag}" + _plain_commit="#commit=${_bleeding_tag}" fi source=("$_winesrcdir"::"${_winesrctarget}${_plain_commit}" diff --git a/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/legacy/sims_3-oldnvidia-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/legacy/sims_3-oldnvidia-c14de4c.patch new file mode 100644 index 000000000..f0c2fc657 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/legacy/sims_3-oldnvidia-c14de4c.patch @@ -0,0 +1,116 @@ +--- a/dlls/wined3d/adapter_gl.c ++++ a/dlls/wined3d/adapter_gl.c +@@ -821,17 +821,6 @@ static BOOL match_broken_arb_fog(const struct wined3d_gl_info *gl_info, struct w + return data[0] != 0x00ff0000 || data[3] != 0x0000ff00; + } + +-static BOOL match_broken_viewport_subpixel_bits(const struct wined3d_gl_info *gl_info, +- struct wined3d_caps_gl_ctx *ctx, const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, +- enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +-{ +- if (!gl_info->supported[ARB_VIEWPORT_ARRAY]) +- return FALSE; +- if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) +- return FALSE; +- return !wined3d_caps_gl_ctx_test_viewport_subpixel_bits(ctx); +-} +- + static BOOL match_no_independent_bit_depths(const struct wined3d_gl_info *gl_info, + struct wined3d_caps_gl_ctx *ctx, const char *gl_renderer, enum wined3d_gl_vendor gl_vendor, + enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device) +@@ -977,15 +966,6 @@ static void quirk_broken_arb_fog(struct wined3d_gl_info *gl_info) + gl_info->quirks |= WINED3D_QUIRK_BROKEN_ARB_FOG; + } + +-static void quirk_broken_viewport_subpixel_bits(struct wined3d_gl_info *gl_info) +-{ +- if (gl_info->supported[ARB_CLIP_CONTROL]) +- { +- TRACE("Disabling ARB_clip_control.\n"); +- gl_info->supported[ARB_CLIP_CONTROL] = FALSE; +- } +-} +- + static void quirk_no_independent_bit_depths(struct wined3d_gl_info *gl_info) + { + gl_info->quirks |= WINED3D_QUIRK_NO_INDEPENDENT_BIT_DEPTHS; +@@ -1125,11 +1105,6 @@ static void fixup_extensions(struct wined3d_gl_info *gl_info, struct wined3d_cap + quirk_broken_arb_fog, + "ARBfp fogstart == fogend workaround" + }, +- { +- match_broken_viewport_subpixel_bits, +- quirk_broken_viewport_subpixel_bits, +- "NVIDIA viewport subpixel bits bug" +- }, + { + match_no_independent_bit_depths, + quirk_no_independent_bit_depths, +--- a/dlls/wined3d/utils.c ++++ a/dlls/wined3d/utils.c +@@ -3795,53 +3795,6 @@ static void init_format_gen_mipmap_info(const struct wined3d_adapter *adapter, + } + } + +-BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) +-{ +- static const struct wined3d_color red = {1.0f, 0.0f, 0.0f, 1.0f}; +- const struct wined3d_gl_info *gl_info = ctx->gl_info; +- static const float offset = -63.0f / 128.0f; +- GLuint texture, fbo; +- DWORD readback[4]; +- unsigned int i; +- +- gl_info->gl_ops.gl.p_glGenTextures(1, &texture); +- gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); +- gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); +- gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ARRAY_SIZE(readback), 1, 0, +- GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); +- gl_info->fbo_ops.glGenFramebuffers(1, &fbo); +- gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo); +- gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, +- GL_TEXTURE_2D, texture, 0); +- checkGLcall("create resources"); +- +- gl_info->gl_ops.gl.p_glClearColor(1.0f, 1.0f, 1.0f, 1.0f); +- gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT); +- GL_EXTCALL(glViewportIndexedf(0, offset, offset, 4.0f, 1.0f)); +- draw_test_quad(ctx, NULL, &red); +- checkGLcall("draw"); +- +- gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, texture); +- gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, +- GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback); +- checkGLcall("readback"); +- +- TRACE("Readback colors are 0x%08x, 0x%08x, 0x%08x, 0x%08x.\n", +- readback[0], readback[1], readback[2], readback[3]); +- +- gl_info->gl_ops.gl.p_glDeleteTextures(1, &texture); +- gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo); +- gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0); +- checkGLcall("delete resources"); +- +- for (i = 0; i < ARRAY_SIZE(readback); ++i) +- { +- if (readback[i] != 0xffff0000) +- return FALSE; +- } +- return TRUE; +-} +- + static float wined3d_adapter_find_polyoffset_scale(struct wined3d_caps_gl_ctx *ctx, GLenum format) + { + const struct wined3d_gl_info *gl_info = ctx->gl_info; +--- a/dlls/wined3d/wined3d_private.h ++++ a/dlls/wined3d/wined3d_private.h +@@ -2710,8 +2710,6 @@ BOOL wined3d_adapter_gl_init_format_info(struct wined3d_adapter *adapter, + BOOL wined3d_adapter_no3d_init_format_info(struct wined3d_adapter *adapter) DECLSPEC_HIDDEN; + UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) DECLSPEC_HIDDEN; + +-BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN; +- + void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext) DECLSPEC_HIDDEN; + + enum wined3d_projection_type + diff --git a/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia index 5ca76af02..a0bd73a9e 100644 --- a/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia +++ b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia @@ -2,8 +2,10 @@ # The Sims 3 fix - reverts 6823abd521c0c12d20d9171fb5ae8b300009d082 to fix Sims 3 on older than 415.xx nvidia drivers - https://bugs.winehq.org/show_bug.cgi?id=45361 if [ "$_sims3_fix" = "true" ]; then - if git merge-base --is-ancestor 83c9e5243a663370296148471628a350ba9422c6 HEAD; then + if git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then _patchname='sims_3-oldnvidia.patch' && _patchmsg="Applied The Sims 3 Debian&co nvidia fix" && nonuser_patcher + elif git merge-base --is-ancestor 83c9e5243a663370296148471628a350ba9422c6 HEAD; then + _patchname='sims_3-oldnvidia-c14de4c.patch' && _patchmsg="Applied The Sims 3 Debian&co nvidia fix" && nonuser_patcher elif git merge-base --is-ancestor 6823abd521c0c12d20d9171fb5ae8b300009d082 HEAD; then _patchname='sims_3-oldnvidia-83c9e52.patch' && _patchmsg="Applied The Sims 3 Debian&co nvidia fix" && nonuser_patcher fi diff --git a/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia.patch b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia.patch index f0c2fc657..0eaffc175 100644 --- a/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia.patch +++ b/wine-tkg-git/wine-tkg-patches/game-specific/sims_3-oldnvidia/sims_3-oldnvidia.patch @@ -105,12 +105,12 @@ --- a/dlls/wined3d/wined3d_private.h +++ a/dlls/wined3d/wined3d_private.h @@ -2710,8 +2710,6 @@ BOOL wined3d_adapter_gl_init_format_info(struct wined3d_adapter *adapter, - BOOL wined3d_adapter_no3d_init_format_info(struct wined3d_adapter *adapter) DECLSPEC_HIDDEN; - UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) DECLSPEC_HIDDEN; + BOOL wined3d_adapter_no3d_init_format_info(struct wined3d_adapter *adapter); + UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount); --BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN; +-BOOL wined3d_caps_gl_ctx_test_viewport_subpixel_bits(struct wined3d_caps_gl_ctx *ctx); - - void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext) DECLSPEC_HIDDEN; + void install_gl_compat_wrapper(struct wined3d_gl_info *gl_info, enum wined3d_gl_extension ext); enum wined3d_projection_type diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/01-non-patch-hotfixes/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/01-non-patch-hotfixes/hotfixes index b44d59f45..e7b1c76bb 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/01-non-patch-hotfixes/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/01-non-patch-hotfixes/hotfixes @@ -12,8 +12,8 @@ if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f7427164877 fi # On Proton, revert uiautomationcore changes conflicting with our tabtip patches -if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_tabtip" = "true" ]; then - _hotfix_mainlinereverts+=(b3a87694a470cae9ed3e9d52a97c13596ab2b354 cd6891962b070708228f1c32bf3480c3ae65a9ca 3bc59a4e9a7d5b76ff533304d4c56bd4e21c6ebd dc79cdc50a2db10c6cdba51ae278c0d6320bf215 bbc187a2b7614417138263277c2a881ec1a29105 8a4ad6096b37acf453bd24d63279887b668a0dc4 ba960ed49954dc5c4a48bf71780a02c83dff8c68 c31e5313184380e1ec31a06ee62d2c9ae971b215 0b487338f373b41aeeaed76a531262db9f4b1b72 21e6fd5076d3b35fb69d7d579e5bf506d3763154 6254f6635cd3b57870e77376df4ff95469e7cf8b 2d8bf67b90ccfbc3d5cfc45bbd68ca666ecd99b4 e8dd3adae72bbe58a66eda8f55ed8df393a9d1b9 88a1e949718cfbffedc46a63a8ad7dad05418dd1 ed1ed6aeb36ec31333e29b829c19c83d07e523b2 bb66ad4459059a338cbf788d64f77cd75986bcab 2b8c8be7bdf7102e472644a44f622bd62fbfe4e3 44aa2ea89fae9324573af49430d9c0795dbac079 23c10c928b68918515b6ec195d90b09ef5936451 e84b95ef6f64a2a267f54e337ed2c5ca37bd3485 5e7356c8dbce777ff76af91ab899c0ba0e38cc2b c928ae56e42e9cccfaeb0228c272a9326c9f1910 8f70e20fbddda976eec8e1d2dd7b718ae66b99b9 511f98280d1ba0ccce63453d4deae67437fff200 abc1f7dadb1f71fc246fa36b6426233a43f98155 01fb2a26d2649666d3879a223a2cf2b779898cfd 51aa53161a11a595a8d0b323ac2b1a84b50a9501 f02ee34d3ae09be8fa12cf6a04d4200d65bf032d c0360db5611cd70f8a00eee77191db546bcc5b3a 41736eb5542561a3db3af639a18c27775e8ce19c 9ea53ec2f6c4d67792507b8a3ed80f8cfffacf98 922706bd1e7ac4da9d77bb5be7f7c395d3374ec1 b4f9954641dcb1cd8ac58e7cc4d56500c1b13945 8719b8dee2a66de446053902ceed01ef5e20a511 67dd36095f12baa39f54919ca8fb65f22778242b 76f20d6a4b1ed779a725ef7278f867d27da4e2a0 af0c9d0b7c99c24d74350fc5578b0d01c6a441b4 389df55dfbcb513d2163cfa7a61e671209c0a75d 7602ad014f697ac7bdd46304ffd708738efff51e ef7e88ea5202bdbb211a2b314768877f07c82938 c375765e63041905967848c10c25d544ca0fdb9c 4bab221b1dff490cdbbafe1e415ca3f2feada0ae 567f00c865fa95c6c19a9c218b7bdc481595f9d5 4ed9f4c2c506f86d917c7c21abe35e047cb0fcfa 2cfcddb2fefaf6b3c26d213ae6be2b0d355ec8f3 c183afbf4c2067a4bc0a66895edca1243d1ab604 cd143fc2fdbfab17bf5c922fd93af56d9b58393b 9967d214561be5b2e10f877cbdb9b97dd7ff24cb 94e0c663c3dd4d1b0b9903968ef8cecf9491f16c 65bca11b9511db10e8f7a2d386c3c56aacbaa81a d13d704f115417997f4fc1258585f24bfd84dd16 272c4a59b0b72b89f5621ef32f008899f4cfceec 2de6b5719e54f9879a550dd2f04740c858dd82be 329664392e94e937ca788fcfc641a283fac97758 59589e11964971864dcc17762c5e1c167ae224bb f36e1b9138aa5865ece2988d641767a4742c81a1 750d11a49ab28597909bd1788cda2bda38b364be 59a629810aa827927b30ffc167b3386b00ae92a1 edcd55bae27d5d6fcac8e4d8b8298cce0dcd0600 a6f428fae6313107c34fb7e43c26d1e602bd0a3d 5d3e444b1cbdbbeeef2b6b7b6508b0ca3dbbd9c1 890faf87dd691df95e55976b02aa7999d3340c41 858e9a4cebe75d10d984a16fce562fd4f79e524f 63814b70d1f6f16640179b07eb5ea8a7c80e713d 8099291fa604504a199b9e63288fb66bdd0a878b 958bba92d2ee1b5a653720647cb83f6233b7bab1 8642e96c19d25306a30874b7c128c93193110d8c 41dd89a8e4529181f8599ab27356728e4af20b1a 858596bbd5dadee8eb877195f1e9152579448299 d7c95765d0b9ebfaa5f424cac0d72576b7d28198 02b87a400225e5b833d0a4b966d20b71549c4ee1 1f86e2c6d9955a084739e586bcbc848e9a8aad14 dadfd3d424ab256677e1a1d3c253f3769f515066 0520531c52fd3256d688affba414291cb2f9f016 cf9d7ee4b4d97e16c0da5fb609372341b965e9df 3106380d5869544d8bd856cd37d748452eb3044d de6f778915167ea3cb48312e90a3e66e13184897 3079c6bebae30be0da439fd1fb9a63523d09b9a0 c97933fe95dd8e5588f500dfca024a46cb7b0da0 c40bccb867c01b1d2e4d846be557f858869e2625 0bc1f6876183b6a695d2660de119c75b38e41cab d83c019102d8dc26e5223cf797b6a72bbea2274d e382997c8b86de60bead165a86d433e050766955 0cea011921304eaa48639fe2f591842289f75fde afb155145ac719cc0efb4b6de12675b473f06187 61fa18aa04e31afeaa42ea768fa63161b427784c 7dc4a1793ea3139ee34448d1c1cec42c46a853d3 93d6b5a4ff0097e9578584b2aaf6a850aef0e9ae bc584afad9e27159214948b5a283d809832d436c ab4a0017e624367d7d0826872290b76bb8f9535a 7840615361c781b9e70f870226fefcc78eb4e424 3fc9493f42a0fca6138fe3778af6cc47e89cc308 350220af30ceb631b03e5872680326dd7cd97647 62d76348b371e0a9f9dd3a67b801cc3c7cc4bc36 7d5708a18224e588ffa456996c4f9f0edee87360 163e9c5b453b3a4255deb4fd1d8da73866a3c0a1 66b6786e8dd5fc28481e6201361a2f3094d395fa ce9ea0260e7a590efde16edceb473a5a12315867 fdd7f3152e4b95552364810060eb33fcb18b2b0b ef27965e822839a02869061746798d7e265ec30b f1d77f18b960d86f6e49014f896d2276a25591c6 28c7a4d1a57c520d918ae3dfe884154784ef2e2e 1989d144c8fa9efd2915fda943152905c585b666 12dfa7425437e5c8f5caab40b7f09beb22d1eab5 f256979be1df085ae9ed1f31ada6f98cbe8c5949 c250b5c777e494ebc0b8b3a0da744b7c5e3ad8b4 e3349cef4326955c80266d0e3a03cb8c156c949c 06344846d5208e5bf8b1f237ba6d0eb312ad05f9 97357ea62247f6c8a05037580abaa2b4af7c70d6 583cbd2383e91af7ed2a35021fa291d2bbaa8b87 1ebccd0f442d19efeeffa7dfacbcd71c296ffc80) +if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_tabtip" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor 5353b2594bf01b8b49c7d7510d0a21d52fae2544 HEAD ); then + _hotfix_mainlinereverts+=(6f3bd0bc825b7d9a7d284d0599235424ebb97f5d b9d10cee89e435858b9b10e610c96a5ef1e88675 3978ff6538839280543ad4523d7ccdca79b86931 68f6a73ad2b438229b00ac31e48a815face2dfc9 eeb098e7b6d4796216969ae284e47734ffedbd34 fdb3c9f93a959b4cbccc9ec60234a0632c36563f 3b68d75f535e717cf3e423614bc6cdd8911c5741 d3d108f9b8dcaa2e4e604afea632038e616ddbbf 29a4e096d76a2e26ff377766c049ddb352751b40 b9410e8c395a9c3a463a1ab0f0fb864fec1de5df 98027dd6f5ecfa11ef2e3fa993a5500a00596046 062584af05b5a7baa0231c98078eaed6cf3262e6 549b4f3db4c064bb9b8b8b41cac4f555dab4539b 64d13cb7ecbd24c55973a440319d2248fbd55675 67496491804953c04427c90821ecab6e5a9f2035 5353b2594bf01b8b49c7d7510d0a21d52fae2544 a4bb7c12b2c19451896cbf1806feb42c71e56b92 f5d562b7899086abe9b2627c6d57c059513ceec4 078a2c88317417307184aeaacdf58ae6ca822578 6b4ad50c83f01d7ae3775828353c2ac97117f415 6adae23d98efd33bd444e9bff7f690dbd25d7fea 5353b2594bf01b8b49c7d7510d0a21d52fae2544 a4bb7c12b2c19451896cbf1806feb42c71e56b92 f5d562b7899086abe9b2627c6d57c059513ceec4 078a2c88317417307184aeaacdf58ae6ca822578 6b4ad50c83f01d7ae3775828353c2ac97117f415 6adae23d98efd33bd444e9bff7f690dbd25d7fea b3a87694a470cae9ed3e9d52a97c13596ab2b354 cd6891962b070708228f1c32bf3480c3ae65a9ca 3bc59a4e9a7d5b76ff533304d4c56bd4e21c6ebd dc79cdc50a2db10c6cdba51ae278c0d6320bf215 bbc187a2b7614417138263277c2a881ec1a29105 8a4ad6096b37acf453bd24d63279887b668a0dc4 ba960ed49954dc5c4a48bf71780a02c83dff8c68 c31e5313184380e1ec31a06ee62d2c9ae971b215 0b487338f373b41aeeaed76a531262db9f4b1b72 21e6fd5076d3b35fb69d7d579e5bf506d3763154 6254f6635cd3b57870e77376df4ff95469e7cf8b 2d8bf67b90ccfbc3d5cfc45bbd68ca666ecd99b4 e8dd3adae72bbe58a66eda8f55ed8df393a9d1b9 88a1e949718cfbffedc46a63a8ad7dad05418dd1 ed1ed6aeb36ec31333e29b829c19c83d07e523b2 bb66ad4459059a338cbf788d64f77cd75986bcab 2b8c8be7bdf7102e472644a44f622bd62fbfe4e3 44aa2ea89fae9324573af49430d9c0795dbac079 23c10c928b68918515b6ec195d90b09ef5936451 e84b95ef6f64a2a267f54e337ed2c5ca37bd3485 5e7356c8dbce777ff76af91ab899c0ba0e38cc2b c928ae56e42e9cccfaeb0228c272a9326c9f1910 8f70e20fbddda976eec8e1d2dd7b718ae66b99b9 511f98280d1ba0ccce63453d4deae67437fff200 abc1f7dadb1f71fc246fa36b6426233a43f98155 01fb2a26d2649666d3879a223a2cf2b779898cfd 51aa53161a11a595a8d0b323ac2b1a84b50a9501 f02ee34d3ae09be8fa12cf6a04d4200d65bf032d c0360db5611cd70f8a00eee77191db546bcc5b3a 41736eb5542561a3db3af639a18c27775e8ce19c 9ea53ec2f6c4d67792507b8a3ed80f8cfffacf98 922706bd1e7ac4da9d77bb5be7f7c395d3374ec1 b4f9954641dcb1cd8ac58e7cc4d56500c1b13945 8719b8dee2a66de446053902ceed01ef5e20a511 67dd36095f12baa39f54919ca8fb65f22778242b 76f20d6a4b1ed779a725ef7278f867d27da4e2a0 af0c9d0b7c99c24d74350fc5578b0d01c6a441b4 389df55dfbcb513d2163cfa7a61e671209c0a75d 7602ad014f697ac7bdd46304ffd708738efff51e ef7e88ea5202bdbb211a2b314768877f07c82938 c375765e63041905967848c10c25d544ca0fdb9c 4bab221b1dff490cdbbafe1e415ca3f2feada0ae 567f00c865fa95c6c19a9c218b7bdc481595f9d5 4ed9f4c2c506f86d917c7c21abe35e047cb0fcfa 2cfcddb2fefaf6b3c26d213ae6be2b0d355ec8f3 c183afbf4c2067a4bc0a66895edca1243d1ab604 cd143fc2fdbfab17bf5c922fd93af56d9b58393b 9967d214561be5b2e10f877cbdb9b97dd7ff24cb 94e0c663c3dd4d1b0b9903968ef8cecf9491f16c 65bca11b9511db10e8f7a2d386c3c56aacbaa81a d13d704f115417997f4fc1258585f24bfd84dd16 272c4a59b0b72b89f5621ef32f008899f4cfceec 2de6b5719e54f9879a550dd2f04740c858dd82be 329664392e94e937ca788fcfc641a283fac97758 59589e11964971864dcc17762c5e1c167ae224bb f36e1b9138aa5865ece2988d641767a4742c81a1 750d11a49ab28597909bd1788cda2bda38b364be 59a629810aa827927b30ffc167b3386b00ae92a1 edcd55bae27d5d6fcac8e4d8b8298cce0dcd0600 a6f428fae6313107c34fb7e43c26d1e602bd0a3d 5d3e444b1cbdbbeeef2b6b7b6508b0ca3dbbd9c1 890faf87dd691df95e55976b02aa7999d3340c41 858e9a4cebe75d10d984a16fce562fd4f79e524f 63814b70d1f6f16640179b07eb5ea8a7c80e713d 8099291fa604504a199b9e63288fb66bdd0a878b 958bba92d2ee1b5a653720647cb83f6233b7bab1 8642e96c19d25306a30874b7c128c93193110d8c 41dd89a8e4529181f8599ab27356728e4af20b1a 858596bbd5dadee8eb877195f1e9152579448299 d7c95765d0b9ebfaa5f424cac0d72576b7d28198 02b87a400225e5b833d0a4b966d20b71549c4ee1 1f86e2c6d9955a084739e586bcbc848e9a8aad14 dadfd3d424ab256677e1a1d3c253f3769f515066 0520531c52fd3256d688affba414291cb2f9f016 cf9d7ee4b4d97e16c0da5fb609372341b965e9df 3106380d5869544d8bd856cd37d748452eb3044d de6f778915167ea3cb48312e90a3e66e13184897 3079c6bebae30be0da439fd1fb9a63523d09b9a0 c97933fe95dd8e5588f500dfca024a46cb7b0da0 c40bccb867c01b1d2e4d846be557f858869e2625 0bc1f6876183b6a695d2660de119c75b38e41cab d83c019102d8dc26e5223cf797b6a72bbea2274d e382997c8b86de60bead165a86d433e050766955 0cea011921304eaa48639fe2f591842289f75fde afb155145ac719cc0efb4b6de12675b473f06187 61fa18aa04e31afeaa42ea768fa63161b427784c 7dc4a1793ea3139ee34448d1c1cec42c46a853d3 93d6b5a4ff0097e9578584b2aaf6a850aef0e9ae bc584afad9e27159214948b5a283d809832d436c ab4a0017e624367d7d0826872290b76bb8f9535a 7840615361c781b9e70f870226fefcc78eb4e424 3fc9493f42a0fca6138fe3778af6cc47e89cc308 350220af30ceb631b03e5872680326dd7cd97647 62d76348b371e0a9f9dd3a67b801cc3c7cc4bc36 7d5708a18224e588ffa456996c4f9f0edee87360 163e9c5b453b3a4255deb4fd1d8da73866a3c0a1 66b6786e8dd5fc28481e6201361a2f3094d395fa ce9ea0260e7a590efde16edceb473a5a12315867 fdd7f3152e4b95552364810060eb33fcb18b2b0b ef27965e822839a02869061746798d7e265ec30b f1d77f18b960d86f6e49014f896d2276a25591c6 28c7a4d1a57c520d918ae3dfe884154784ef2e2e 1989d144c8fa9efd2915fda943152905c585b666 12dfa7425437e5c8f5caab40b7f09beb22d1eab5 f256979be1df085ae9ed1f31ada6f98cbe8c5949 c250b5c777e494ebc0b8b3a0da744b7c5e3ad8b4 e3349cef4326955c80266d0e3a03cb8c156c949c 06344846d5208e5bf8b1f237ba6d0eb312ad05f9 97357ea62247f6c8a05037580abaa2b4af7c70d6 583cbd2383e91af7ed2a35021fa291d2bbaa8b87 1ebccd0f442d19efeeffa7dfacbcd71c296ffc80) fi # Workaround broken staging commit 5117eec. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/ealink_fixup.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/ealink_fixup.patch new file mode 100644 index 000000000..2a8829f81 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/ealink_fixup.patch @@ -0,0 +1,302 @@ +From e229a2e3305c2395fb970f61e8d2d6a9499aaafc Mon Sep 17 00:00:00 2001 +From: Alex Henrie +Date: Sun, 7 May 2023 22:34:15 -0600 +Subject: [PATCH 1/2] winemenubuilder: Create .desktop files for programs that + open URIs. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22904 +--- + programs/winemenubuilder/winemenubuilder.c | 164 ++++++++++++--------- + 1 file changed, 96 insertions(+), 68 deletions(-) + +diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c +index ae657d87bb1..d8ab78d1de8 100644 +--- a/programs/winemenubuilder/winemenubuilder.c ++++ b/programs/winemenubuilder/winemenubuilder.c +@@ -1833,10 +1833,13 @@ static BOOL has_association_changed(LPCWSTR extensionW, const WCHAR *mimeType, c + ret = TRUE; + heap_free(value); + +- value = reg_get_valW(assocKey, extensionW, L"ProgID"); +- if (!value || wcscmp(value, progId)) +- ret = TRUE; +- heap_free(value); ++ if (progId) ++ { ++ value = reg_get_valW(assocKey, extensionW, L"ProgID"); ++ if (!value || wcscmp(value, progId)) ++ ret = TRUE; ++ heap_free(value); ++ } + + value = reg_get_valW(assocKey, extensionW, L"AppName"); + if (!value || wcscmp(value, appName)) +@@ -1880,7 +1883,7 @@ static void update_association(LPCWSTR extension, const WCHAR *mimeType, const W + } + + RegSetValueExW(subkey, L"MimeType", 0, REG_SZ, (const BYTE*) mimeType, (lstrlenW(mimeType) + 1) * sizeof(WCHAR)); +- RegSetValueExW(subkey, L"ProgID", 0, REG_SZ, (const BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR)); ++ if (progId) RegSetValueExW(subkey, L"ProgID", 0, REG_SZ, (const BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR)); + RegSetValueExW(subkey, L"AppName", 0, REG_SZ, (const BYTE*) appName, (lstrlenW(appName) + 1) * sizeof(WCHAR)); + RegSetValueExW(subkey, L"DesktopFile", 0, REG_SZ, (const BYTE*) desktopFile, (lstrlenW(desktopFile) + 1) * sizeof(WCHAR)); + if (openWithIcon) +@@ -1964,12 +1967,16 @@ static BOOL write_freedesktop_mime_type_entry(const WCHAR *packages_dir, const W + return ret; + } + +-static BOOL is_extension_banned(LPCWSTR extension) ++static BOOL is_type_banned(const WCHAR *win_type) + { + /* These are managed through external tools like wine.desktop, to evade malware created file type associations */ +- if (!wcsicmp(extension, L".com") || +- !wcsicmp(extension, L".exe") || +- !wcsicmp(extension, L".msi")) ++ if (!wcsicmp(win_type, L".com") || ++ !wcsicmp(win_type, L".exe") || ++ !wcsicmp(win_type, L".msi")) ++ return TRUE; ++ /* Associating a program with the file URI scheme is like associating it with all file types, which is not allowed ++ * for the same reasons */ ++ if (!wcsicmp(win_type, L"file")) + return TRUE; + return FALSE; + } +@@ -2043,11 +2050,15 @@ static BOOL write_freedesktop_association_entry(const WCHAR *desktopPath, const + if (prefix) + { + char *path = wine_get_unix_file_name( prefix ); +- fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start /ProgIDOpen %s %%f\n", path, escape(progId)); ++ fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start ", path); + heap_free( path ); + } + else +- fprintf(desktop, "Exec=wine start /ProgIDOpen %s %%f\n", escape(progId)); ++ fprintf(desktop, "Exec=wine start "); ++ if (progId) /* file association */ ++ fprintf(desktop, "/ProgIDOpen %s %%f\n", escape(progId)); ++ else /* protocol association */ ++ fprintf(desktop, "%%u\n"); + fprintf(desktop, "NoDisplay=true\n"); + fprintf(desktop, "StartupNotify=true\n"); + if (openWithIcon) +@@ -2075,12 +2086,19 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic + + for (i = 0; ; i++) + { +- WCHAR *extensionW; ++ WCHAR *winTypeW; ++ BOOL isProtocolType = FALSE; + +- if (!(extensionW = reg_enum_keyW(HKEY_CLASSES_ROOT, i))) ++ if (!(winTypeW = reg_enum_keyW(HKEY_CLASSES_ROOT, i))) + break; + +- if (extensionW[0] == '.' && !is_extension_banned(extensionW)) ++ if (winTypeW[0] != '.') ++ { ++ if (RegGetValueW(HKEY_CLASSES_ROOT, winTypeW, L"URL Protocol", RRF_RT_ANY, NULL, NULL, NULL) == ERROR_SUCCESS) ++ isProtocolType = TRUE; ++ } ++ ++ if ((winTypeW[0] == '.' || isProtocolType) && !is_type_banned(winTypeW)) + { + WCHAR *commandW = NULL; + WCHAR *executableW = NULL; +@@ -2094,7 +2112,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic + WCHAR *mimeProgId = NULL; + struct rb_string_entry *entry; + +- commandW = assoc_query(ASSOCSTR_COMMAND, extensionW, L"open"); ++ commandW = assoc_query(ASSOCSTR_COMMAND, winTypeW, L"open"); + if (commandW == NULL) + /* no command => no application is associated */ + goto end; +@@ -2191,7 +2219,7 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic + heap_free(mimeType); + heap_free(progIdW); + } +- heap_free(extensionW); ++ heap_free(winTypeW); + } + + wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL); +-- +GitLab + +diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c +index bb5dcf533f2..42398f20b8e 100644 +--- a/programs/winemenubuilder/winemenubuilder.c ++++ b/programs/winemenubuilder/winemenubuilder.c +@@ -1969,43 +1969,69 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic + WCHAR *mimeProgId = NULL; + struct rb_string_entry *entry; + +- wcslwr(extensionW); +- friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL); ++ iconW = assoc_query(ASSOCSTR_DEFAULTICON, winTypeW, NULL); + +- iconW = assoc_query(ASSOCSTR_DEFAULTICON, extensionW, NULL); ++ if (isProtocolType) ++ { ++ mimeType = heap_wprintf(L"x-scheme-handler/%s", winTypeW); ++ } ++ else ++ { ++ wcslwr(winTypeW); ++ friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, winTypeW, NULL); + +- contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL); +- if (contentTypeW) +- wcslwr(contentTypeW); ++ contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, winTypeW, NULL); ++ if (contentTypeW) ++ wcslwr(contentTypeW); + +- mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, extensionW); ++ mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, winTypeW); + +- if (mimeType == NULL) +- { +- if (contentTypeW != NULL && wcschr(contentTypeW, '/')) +- mimeType = xwcsdup(contentTypeW); +- else if (!(mimeType = get_special_mime_type(extensionW))) +- mimeType = heap_wprintf(L"application/x-wine-extension-%s", &extensionW[1]); +- +- /* GNOME seems to ignore the tag in MIME packages, +- * and the default name is more intuitive anyway. +- */ +- if (iconW) ++ if (mimeType == NULL) + { +- WCHAR *flattened_mime = slashes_to_minuses(mimeType); +- int index = 0; +- WCHAR *comma = wcsrchr(iconW, ','); +- if (comma) ++ if (contentTypeW != NULL && wcschr(contentTypeW, '/')) ++ mimeType = xwcsdup(contentTypeW); ++ else if (!(mimeType = get_special_mime_type(winTypeW))) ++ mimeType = heap_wprintf(L"application/x-wine-extension-%s", &winTypeW[1]); ++ ++ /* GNOME seems to ignore the tag in MIME packages, ++ * and the default name is more intuitive anyway. ++ */ ++ if (iconW) + { +- *comma = 0; +- index = wcstol(comma + 1, NULL, 10); ++ WCHAR *flattened_mime = slashes_to_minuses(mimeType); ++ int index = 0; ++ WCHAR *comma = wcsrchr(iconW, ','); ++ if (comma) ++ { ++ *comma = 0; ++ index = wcstol(comma + 1, NULL, 10); ++ } ++ extract_icon(iconW, index, flattened_mime, FALSE); ++ heap_free(flattened_mime); + } +- extract_icon(iconW, index, flattened_mime, FALSE); +- heap_free(flattened_mime); ++ ++ write_freedesktop_mime_type_entry(packages_dir, winTypeW, mimeType, friendlyDocNameW); ++ hasChanged = TRUE; ++ } ++ ++ progIdW = reg_get_valW(HKEY_CLASSES_ROOT, winTypeW, NULL); ++ if (!progIdW) goto end; /* no progID => not a file type association */ ++ ++ /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */ ++ mimeProgId = heap_wprintf(L"%s=>%s", mimeType, progIdW); ++ if (wine_rb_get(&mimeProgidTree, mimeProgId)) ++ { ++ heap_free(mimeProgId); ++ goto end; ++ } ++ entry = xmalloc(sizeof(struct rb_string_entry)); ++ entry->string = mimeProgId; ++ if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry)) ++ { ++ WINE_ERR("error updating rb tree\n"); ++ goto end; + } + +- write_freedesktop_mime_type_entry(packages_dir, extensionW, mimeType, friendlyDocNameW); +- hasChanged = TRUE; + } + + commandW = assoc_query(ASSOCSTR_COMMAND, winTypeW, L"open"); +@@ -2013,39 +2039,24 @@ static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applic + /* no command => no application is associated */ + goto end; + +- executableW = assoc_query(ASSOCSTR_EXECUTABLE, extensionW, L"open"); ++ executableW = assoc_query(ASSOCSTR_EXECUTABLE, winTypeW, L"open"); + if (executableW) + openWithIcon = compute_native_identifier(0, executableW, NULL); + +- friendlyAppName = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, L"open"); ++ friendlyAppName = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, winTypeW, L"open"); + if (!friendlyAppName) friendlyAppName = L"A Wine application"; + +- progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL); +- if (!progIdW) goto end; /* no progID => not a file type association */ +- +- /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */ +- mimeProgId = heap_wprintf(L"%s=>%s", mimeType, progIdW); +- if (wine_rb_get(&mimeProgidTree, mimeProgId)) +- { +- heap_free(mimeProgId); +- goto end; +- } +- entry = xmalloc(sizeof(struct rb_string_entry)); +- entry->string = mimeProgId; +- if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry)) +- { +- WINE_ERR("error updating rb tree\n"); +- goto end; +- } +- +- if (has_association_changed(extensionW, mimeType, progIdW, friendlyAppName, openWithIcon)) ++ if (has_association_changed(winTypeW, mimeType, progIdW, friendlyAppName, openWithIcon)) + { +- WCHAR *desktopPath = heap_wprintf(L"%s\\wine-extension-%s.desktop", +- applications_dir, extensionW + 1 ); ++ WCHAR *desktopPath; ++ if (isProtocolType) ++ desktopPath = heap_wprintf(L"%s\\wine-protocol-%s.desktop", applications_dir, winTypeW); ++ else ++ desktopPath = heap_wprintf(L"%s\\wine-extension-%s.desktop", applications_dir, winTypeW + 1); + if (write_freedesktop_association_entry(desktopPath, friendlyAppName, mimeType, progIdW, openWithIcon)) + { + hasChanged = TRUE; +- update_association(extensionW, mimeType, progIdW, friendlyAppName, desktopPath, openWithIcon); ++ update_association(winTypeW, mimeType, progIdW, friendlyAppName, desktopPath, openWithIcon); + } + heap_free(desktopPath); + } + +From e3395e4bb39eb541f3c4aa2fec96ac6c640efdca Mon Sep 17 00:00:00 2001 +From: Alex Henrie +Date: Sun, 7 May 2023 22:34:15 -0600 +Subject: [PATCH 2/2] winecfg: Mention protocol associations. + +--- + programs/winecfg/winecfg.rc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc +index b2b51ffbf70..48fe0b9f9f0 100644 +--- a/programs/winecfg/winecfg.rc ++++ b/programs/winecfg/winecfg.rc +@@ -307,7 +307,7 @@ BEGIN + CONTROL "",IDC_SYSPARAM_SIZE_UD,UPDOWN_CLASSA,UDS_SETBUDDYINT | UDS_ALIGNRIGHT | WS_DISABLED, 185,75,15,13 + + GROUPBOX "MIME types",IDC_STATIC,8,95,244,23 +- CONTROL "Manage file &associations",IDC_ENABLE_FILE_ASSOCIATIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,105,230,10 ++ CONTROL "Manage file and protocol &associations",IDC_ENABLE_FILE_ASSOCIATIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,105,230,10 + + PUSHBUTTON "&Font...",IDC_SYSPARAM_FONT,190,75,55,13,WS_DISABLED + GROUPBOX "Folders",IDC_STATIC,8,120,244,94 +-- +GitLab + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/hotfix-guild_wars_2.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/hotfix-guild_wars_2.patch new file mode 100644 index 000000000..08a56f60a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/hotfix-guild_wars_2.patch @@ -0,0 +1,42 @@ +From 7bc7829db283134946d1c7ae0468ddc1bbf993a0 Mon Sep 17 00:00:00 2001 +From: deltaconnected +Date: Thu, 26 Aug 2021 03:06:23 +0200 +Subject: [PATCH] ntdll: Use a critical section for setting the newly created timer in RtlCreateTimer + +In RtlCreateTimer, NewTimer is being set after RtlLeaveCriticalSection, which +seems to allow callbacks created with DueTime == 0 to execute and finish and +delete an invalid timer before the scheduling thread is switched back. + +Fixes crashes inside DeleteTimerQueueEx and DeleteTimerQueueTimer for +Guild Wars 2 with Arcdps (https://www.deltaconnected.com/arcdps/) + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51683 +--- + dlls/ntdll/threadpool.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c +index ca323919d05..20096573c2b 100644 +--- a/dlls/ntdll/threadpool.c ++++ b/dlls/ntdll/threadpool.c +@@ -936,12 +936,13 @@ NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue, + if (q->quit) + status = STATUS_INVALID_HANDLE; + else ++ { ++ *NewTimer = t; + queue_add_timer(t, queue_current_time() + DueTime, TRUE); ++ } + RtlLeaveCriticalSection(&q->cs); + +- if (status == STATUS_SUCCESS) +- *NewTimer = t; +- else ++ if (status != STATUS_SUCCESS) + RtlFreeHeap(GetProcessHeap(), 0, t); + + return status; +-- +2.33.0 + + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix-70.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix-70.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix-80.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix-80.patch new file mode 100755 index 000000000..787fa5a56 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/game-patches/killer-instinct-winevulkan_fix-80.patch @@ -0,0 +1,39 @@ +From 252d6abc7b52a5f506c7fc621c4469d7fd0252f7 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 23 Oct 2020 17:11:18 +0300 +Subject: [PATCH] winevulkan: HACK: Set default mxcsr for + vkEnumeratePhysicalDevices() call. + +Workaround Killer Instinct crash on start with AMD GPU. +--- + dlls/winevulkan/vulkan.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c +index 6e2a5b01359..ca080798f64 100644 +--- a/dlls/winevulkan/vulkan.c ++++ b/dlls/winevulkan/vulkan.c +@@ -1042,6 +1042,7 @@ VkResult wine_vkCreateInstance(const VkInstanceCreateInfo *create_info, + VkInstance client_instance = client_ptr; + VkInstanceCreateInfo create_info_host; + const VkApplicationInfo *app_info; ++ uint32_t new_mxcsr, old_mxcsr; + struct conversion_context ctx; + struct wine_instance *object; + VkResult res; +@@ -694,7 +695,12 @@ VkResult WINAPI wine_vkCreateInstance(const VkInstanceCreateInfo *create_info, + * the native physical devices and present those to the application. + * Cleanup happens as part of wine_vkDestroyInstance. + */ ++ __asm__ volatile("stmxcsr %0" : "=m"(old_mxcsr)); ++ new_mxcsr = 0x1f80; ++ __asm__ volatile("ldmxcsr %0" : : "m"(new_mxcsr)); + res = wine_vk_instance_load_physical_devices(object); ++ __asm__ volatile("ldmxcsr %0" : : "m"(old_mxcsr)); ++ TRACE("old_mxcsr %#x.\n", old_mxcsr); + if (res != VK_SUCCESS) + { + ERR("Failed to load physical devices, res=%d\n", res); +-- +2.26.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/hotfixes index ee2c31a48..8b10f2a0d 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/GE/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/GE/hotfixes @@ -15,8 +15,23 @@ _GE() { msg2 "GE patches..." _patchname="assettocorsa-hud.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch #_patchname="mk11.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch - _patchname="killer-instinct-winevulkan_fix.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch - _patchname="FFVII-and-SpecialK-powerprof.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + if [[ "$_plain_version" = *_8.0 ]]; then + _patchname="killer-instinct-winevulkan_fix-80.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + #_patchname="ealink_fixup.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + _patchname="hotfix-guild_wars_2.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + else + _patchname="killer-instinct-winevulkan_fix-70.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + _patchname="FFVII-and-SpecialK-powerprof.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/game-patches" _apply_GE_patch + _patchname="unity_crash_hotfix.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch + _patchname="Fix-regression-introduced-by-0e7fd41.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/upstream" _apply_GE_patch + #_patchname="15aa8c6-fix-star-citizen-bug-52956.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/upstream" _apply_GE_patch + _patchname="0001-winex11.drv-Define-ControlMask-when-not-available.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch + _patchname="0002-include-Add-THREAD_POWER_THROTTLING_STATE-type.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch + _patchname="0003-ntdll-Fake-success-for-ThreadPowerThrottlingState.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch + # OW2 crash fix cherry pick - c7c729c0db7c3e246182a890de4c606f3e394d0e + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/ow2/c7c729c0) + _patchname="c7c729c0.mypatch" _patchpath="$_where/wine-tkg-patches/hotfixes/ow2" _patchmessage="- Fix for OW2 crashing after a few seconds" _apply_GE_patch + fi if [ "$_GE_FSR" = "true" ]; then if [[ "$_LOCAL_PRESET" = valve-exp-bleeding* ]]; then @@ -24,22 +39,15 @@ _GE() { elif [[ "$_LOCAL_PRESET" = valve-exp* ]]; then _ge_subpath="/exp" fi - _patchname="48-proton-fshack_amd_fsr.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch - _patchname="65-proton-fake_current_res_patches.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch - _patchname="69-proton-fsr-add-329-res.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch - _patchname="70-proton-add_fsr_res_by_aspect_ratio.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch - _patchname="71-invert-fsr-logic.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch - _patchname="72-fsr-use-balanced-default-mode.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + if [[ "$_plain_version" = *_8.0 ]]; then + _patchname="proton-fsr-80.mypatch" _patchpath="$_where/wine-tkg-patches/hotfixes/valve" _apply_GE_patch + else + _patchname="48-proton-fshack_amd_fsr.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + _patchname="65-proton-fake_current_res_patches.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + _patchname="69-proton-fsr-add-329-res.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + _patchname="70-proton-add_fsr_res_by_aspect_ratio.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + _patchname="71-invert-fsr-logic.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + _patchname="72-fsr-use-balanced-default-mode.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/proton$_ge_subpath" _apply_GE_patch + fi fi - - _patchname="unity_crash_hotfix.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch - _patchname="Fix-regression-introduced-by-0e7fd41.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/upstream" _apply_GE_patch - #_patchname="15aa8c6-fix-star-citizen-bug-52956.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/upstream" _apply_GE_patch - _patchname="0001-winex11.drv-Define-ControlMask-when-not-available.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch - _patchname="0002-include-Add-THREAD_POWER_THROTTLING_STATE-type.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch - _patchname="0003-ntdll-Fake-success-for-ThreadPowerThrottlingState.patch" _patchpath="$_where/wine-tkg-patches/hotfixes/GE/wine-hotfixes/pending" _apply_GE_patch - - # OW2 crash fix cherry pick - c7c729c0db7c3e246182a890de4c606f3e394d0e - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/ow2/c7c729c0) - _patchname="c7c729c0.mypatch" _patchpath="$_where/wine-tkg-patches/hotfixes/ow2" _patchmessage="- Fix for OW2 crashing after a few seconds" _apply_GE_patch } diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/hotfixes index 329719a29..c60e3221f 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/hotfixes @@ -2,9 +2,15 @@ # Fix NosTale mouse bug (very big thanks to Morsisko, NosApki team and Linux community) - https://github.com/PSzczepanski1996/proton-ge-nostale/commit/a4078f03775d0a2cc45c7fe048893ab33fc6acfc if [ "$_unfrog" = "true" ]; then - warning "Hotfix: Fix NosTale mouse bug" - cp "$_where"/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix_proton.mypatch "$_where"/ + if [[ "$_plain_version" != *_8.0 ]]; then + warning "Hotfix: Fix NosTale mouse bug" + cp "$_where"/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix_proton.mypatch "$_where"/ + fi elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 879ef36148b489f82a3d99d2113b01eb7eb985a1 HEAD ); then warning "Hotfix: Fix NosTale mouse bug" - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix) + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 5127bedd54716a773fe079e19a4e38891b939671 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix) + else + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix-5127bed) + fi fi diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix-5127bed.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix-5127bed.mypatch new file mode 100644 index 000000000..b2e955a2d --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix-5127bed.mypatch @@ -0,0 +1,10 @@ +--- a/dlls/user32/Makefile.in ++++ b/dlls/user32/Makefile.in +@@ -4,6 +4,7 @@ IMPORTLIB = user32 + IMPORTS = $(PNG_PE_LIBS) gdi32 version sechost advapi32 kernelbase win32u + EXTRAINCL = $(PNG_PE_CFLAGS) + DELAYIMPORTS = setupapi imm32 ++i386_EXTRADLLFLAGS = -Wl,--image-base,0x7e410000 + + C_SRCS = \ + button.c \ diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix.mypatch index b2e955a2d..b828958fe 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix.mypatch +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/NosTale/nostale_mouse_fix.mypatch @@ -3,7 +3,7 @@ @@ -4,6 +4,7 @@ IMPORTLIB = user32 IMPORTS = $(PNG_PE_LIBS) gdi32 version sechost advapi32 kernelbase win32u EXTRAINCL = $(PNG_PE_CFLAGS) - DELAYIMPORTS = setupapi imm32 + DELAYIMPORTS = imm32 +i386_EXTRADLLFLAGS = -Wl,--image-base,0x7e410000 C_SRCS = \ diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/hotfixes index b327bc19c..5616aaceb 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/hotfixes @@ -1,9 +1,15 @@ #!/bin/bash # Patch opencl.h header to it's correct location without breaking successive autoconf call, as it was done by the sed workaround -if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9207927d950ce8ab41a6d8c25cfa593ce770d5cf HEAD ); then +if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 4a4d35244120ce7d2abdd207a8a81a0b03c349c2 HEAD ); then warning "Hotfix: Fix for CL/opencl.h header recognition" _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup) +elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f74c4af257e0856e6607d2823e4f03109111a4b6 HEAD ); then + warning "Hotfix: Fix for CL/opencl.h header recognition" + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-4a4d352) +elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9207927d950ce8ab41a6d8c25cfa593ce770d5cf HEAD ); then + warning "Hotfix: Fix for CL/opencl.h header recognition" + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-f74c4af) elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 45eca854c4fab7e864deebf9a51c74147884ebc8 HEAD ); then warning "Hotfix: Fix for CL/opencl.h header recognition" _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-9207927) diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-4a4d352.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-4a4d352.mypatch new file mode 100644 index 000000000..222231415 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-4a4d352.mypatch @@ -0,0 +1,52 @@ +From 7b06d5e9906ebb92d4a1dae2fb04120e9c1b60dd Mon Sep 17 00:00:00 2001 +From: llde +Date: Thu, 17 Nov 2022 15:25:45 +0100 +Subject: [PATCH] Explicitly define HAVE_OPENCL_OPENCL_H when checking opencl.h + header and use Cl/opencl.h instead + +--- + configure.ac | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 2bb09f618c7..9cc74660864 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -50,7 +50,7 @@ AC_ARG_WITH(netapi, AS_HELP_STRING([--without-netapi],[do not use the Samba N + AC_ARG_WITH(openal, AS_HELP_STRING([--without-openal],[do not use OpenAL]), + [if test "x$withval" = "xno"; then ac_cv_header_AL_al_h=no; ac_cv_header_OpenAL_al_h=no; fi]) + AC_ARG_WITH(opencl, AS_HELP_STRING([--without-opencl],[do not use OpenCL]), +- [if test "x$withval" = "xno"; then ac_cv_header_CL_cl_h=no; ac_cv_header_OpenCL_opencl_h=no; fi]) ++ [if test "x$withval" = "xno"; then ac_cv_header_CL_cl_h=no; ac_cv_header_CL_opencl_h=no; fi]) + AC_ARG_WITH(opengl, AS_HELP_STRING([--without-opengl],[do not use OpenGL])) + AC_ARG_WITH(osmesa, AS_HELP_STRING([--without-osmesa],[do not use the OSMesa library])) + AC_ARG_WITH(oss, AS_HELP_STRING([--without-oss],[do not use the OSS sound support])) +@@ -430,7 +430,6 @@ AC_CHECK_HEADERS(\ + AC_CHECK_HEADERS(\ + CL/cl.h \ + EGL/egl.h \ +- OpenCL/opencl.h \ + PCSC/pcsclite.h \ + arpa/inet.h \ + arpa/nameser.h \ +@@ -515,6 +514,8 @@ AC_CHECK_HEADERS(\ + valgrind/memcheck.h \ + valgrind/valgrind.h + ) ++dnl Wine use HAVE_OPENCL_OPENCL_H to condition, explicit it. ++AC_CHECK_HEADER(CL/opencl.h,[AC_DEFINE(HAVE_OPENCL_OPENCL_H, 1, [Define to 1 if you have the header file.])]) + WINE_HEADER_MAJOR() + AC_HEADER_STAT() + +@@ -754,7 +755,7 @@ case $host_os in + AC_DEFINE_UNQUOTED(HAVE_OPENAL,1,[Define to 1 if OpenAL is available]) + ac_cv_lib_openal=yes + fi +- if test "$ac_cv_header_OpenCL_opencl_h" = "yes" ++ if test "$ac_cv_header_CL_opencl_h" = "yes" + then + AC_SUBST(OPENCL_LIBS,"-framework OpenCL") + ac_cv_lib_OpenCL_clGetPlatformInfo=yes +-- +2.38.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-f74c4af.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-f74c4af.mypatch new file mode 100644 index 000000000..794d67b07 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/autoconf-opencl-hotfix/opencl-fixup-f74c4af.mypatch @@ -0,0 +1,52 @@ +From 7b06d5e9906ebb92d4a1dae2fb04120e9c1b60dd Mon Sep 17 00:00:00 2001 +From: llde +Date: Thu, 17 Nov 2022 15:25:45 +0100 +Subject: [PATCH] Explicitly define HAVE_OPENCL_OPENCL_H when checking opencl.h + header and use Cl/opencl.h instead + +--- + configure.ac | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 2bb09f618c7..9cc74660864 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -50,7 +50,7 @@ AC_ARG_WITH(netapi, AS_HELP_STRING([--without-netapi],[do not use the Samba N + AC_ARG_WITH(openal, AS_HELP_STRING([--without-openal],[do not use OpenAL]), + [if test "x$withval" = "xno"; then ac_cv_header_AL_al_h=no; ac_cv_header_OpenAL_al_h=no; fi]) + AC_ARG_WITH(opencl, AS_HELP_STRING([--without-opencl],[do not use OpenCL]), +- [if test "x$withval" = "xno"; then ac_cv_header_CL_cl_h=no; ac_cv_header_OpenCL_opencl_h=no; fi]) ++ [if test "x$withval" = "xno"; then ac_cv_header_CL_cl_h=no; ac_cv_header_CL_opencl_h=no; fi]) + AC_ARG_WITH(opengl, AS_HELP_STRING([--without-opengl],[do not use OpenGL])) + AC_ARG_WITH(osmesa, AS_HELP_STRING([--without-osmesa],[do not use the OSMesa library])) + AC_ARG_WITH(oss, AS_HELP_STRING([--without-oss],[do not use the OSS sound support])) +@@ -430,7 +430,6 @@ AC_CHECK_HEADERS(\ + AC_CHECK_HEADERS(\ + CL/cl.h \ + EGL/egl.h \ +- OpenCL/opencl.h \ + arpa/inet.h \ + arpa/nameser.h \ + asm/types.h \ +@@ -515,6 +514,8 @@ AC_CHECK_HEADERS(\ + valgrind/memcheck.h \ + valgrind/valgrind.h + ) ++dnl Wine use HAVE_OPENCL_OPENCL_H to condition, explicit it. ++AC_CHECK_HEADER(CL/opencl.h,[AC_DEFINE(HAVE_OPENCL_OPENCL_H, 1, [Define to 1 if you have the header file.])]) + WINE_HEADER_MAJOR() + AC_HEADER_STAT() + +@@ -754,7 +755,7 @@ case $host_os in + AC_DEFINE_UNQUOTED(HAVE_OPENAL,1,[Define to 1 if OpenAL is available]) + ac_cv_lib_openal=yes + fi +- if test "$ac_cv_header_OpenCL_opencl_h" = "yes" ++ if test "$ac_cv_header_CL_opencl_h" = "yes" + then + AC_SUBST(OPENCL_LIBS,"-framework OpenCL") + ac_cv_lib_OpenCL_clGetPlatformInfo=yes +-- +2.38.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/earlyhotfixer b/wine-tkg-git/wine-tkg-patches/hotfixes/earlyhotfixer index dc2f07504..9d13f443c 100755 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/earlyhotfixer +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/earlyhotfixer @@ -24,7 +24,9 @@ if [ -d "${srcdir}"/"${_stgsrcdir}" ]; then _steamvr_support="true" # Fixes for 32-bit ldap, futex_waitv outside of Valve's container - cp "$_where"/wine-tkg-patches/hotfixes/valve/fixes/*.mypatch "$_where"/ + if [[ "$_plain_version" != *_8.0 ]]; then + cp "$_where"/wine-tkg-patches/hotfixes/valve/fixes/*.mypatch "$_where"/ + fi if [[ ${_community_patches[*]} =~ "amd_fsr_fshack-alternative.mypatch" ]]; then _community_patches=$(echo $_community_patches | sed "s/amd_fsr_fshack.mypatch//g" | tr -s " ") @@ -35,7 +37,11 @@ if [ -d "${srcdir}"/"${_stgsrcdir}" ]; then fi if [[ ${_community_patches[*]} =~ "amd_fsr_fshack.mypatch" ]]; then - cp "$_where"/wine-tkg-patches/hotfixes/valve/proton-fsr.mypatch "$_where"/ + if [[ "$_plain_version" = *_8.0 ]]; then + cp "$_where"/wine-tkg-patches/hotfixes/valve/proton-fsr-80.mypatch "$_where"/ + else + cp "$_where"/wine-tkg-patches/hotfixes/valve/proton-fsr-70.mypatch "$_where"/ + fi fi if [[ ${_community_patches[*]} =~ "wine_wayland_driver.mypatch" ]]; then @@ -273,6 +279,13 @@ EOM fi fi + # Disable problematic syscall emulation patchset in semi-recent trees that enable it (for new-style WoW64 builds) + # This workarounds the "Internal error" message when running a 32-bit program in the new-style WoW64 mode + if ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor 2e9f238732289907b4f07335d826ac3e7882f5ba HEAD && [ -e "${srcdir}/${_stgsrcdir}/patches/ntdll-Syscall_Emulation/definition" ] && ! grep -Fxq 'Disabled: True' "${srcdir}/${_stgsrcdir}/patches/ntdll-Syscall_Emulation/definition" ) && [ "${_NOLIB32}" = "wow64" ]; then + warning "Disable ntdll-Syscall_Emulation patchset for WoW64 builds (on staging 2e9f2387+)" + _staging_args+=(-W ntdll-Syscall_Emulation) + fi + # Esync is broken on staging commit dc77e28, breaking fsync as a result. Some hunks are getting unordered due to similar contexts. So let's add a bit more context as a fix. if ( cd "${srcdir}"/"${_stgsrcdir}" && [ "$(git rev-parse HEAD)" = "dc77e28b0f7d6fdb11dafacb73b9889545359572" ] ); then warning "Fix eventfd_synchronization on staging dc77e28" diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/hotfixer b/wine-tkg-git/wine-tkg-patches/hotfixes/hotfixer index ea5578f5c..f5a5918a7 100755 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/hotfixer +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/hotfixer @@ -64,6 +64,7 @@ if [ "$_unfrog" != "true" ]; then _hotfix="larger_def_heap" _hotfix_boundary="1d65bc06b303dc19307c1a1c26603d690ffa7f12" _hotfix_import _hotfix="a2395ecf" _hotfix_boundary="a42b22e027e2be20f7f3a924eddeee92b7bbc99d" _hotfix_import _hotfix="1e35966" _hotfix_boundary="7b51216198237c04a8994cda1bdb45fdb4482b32" _hotfix_import + _hotfix="staging_aeddc19_SOURCES" _hotfix_boundary="137638e185e9302602bcd9cc796d7bcfa03caee5" _hotfix_import else _hotfix="valve" _hotfix_boundary="" _hotfix_import _hotfix="GE" _hotfix_boundary="" _hotfix_import diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93-6bcdbbe.myearlyrevert b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93-6bcdbbe.myearlyrevert new file mode 100644 index 000000000..01507070c --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93-6bcdbbe.myearlyrevert @@ -0,0 +1,62 @@ +diff --git a/include/Makefile.in b/include/Makefile.in +index ef4a6d55558f..940106952141 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -672,6 +674,7 @@ SOURCES = \ + snmp.h \ + softpub.h \ + spatialaudioclient.idl \ ++ specstrings.h \ + sperror.h \ + sql.h \ + sqlext.h \ +diff --git a/include/ntdef.h b/include/ntdef.h +index 68ced669baac..74c88ba572be 100644 +--- a/include/ntdef.h ++++ b/include/ntdef.h +@@ -20,6 +20,7 @@ + #define _NTDEF_ + + #include ++#include + + #ifdef __cplusplus + extern "C" { +diff --git a/include/rpcdce.h b/include/rpcdce.h +index 2b289b265507..79418f8084cf 100644 +--- a/include/rpcdce.h ++++ b/include/rpcdce.h +@@ -33,6 +33,8 @@ extern "C" { + #define OPTIONAL + #endif + ++#include ++ + #ifndef GUID_DEFINED + #include + #endif +diff --git a/include/winerror.h b/include/winerror.h +index f22b24f267be..377c61e724b1 100644 +--- a/include/winerror.h ++++ b/include/winerror.h +@@ -19,6 +19,8 @@ + #ifndef __WINE_WINERROR_H + #define __WINE_WINERROR_H + ++#include ++ + #define FACILITY_NULL 0 + #define FACILITY_RPC 1 + #define FACILITY_DISPATCH 2 +diff --git a/include/winnt.h b/include/winnt.h +index 53d9af8bf960..58f1267773dd 100644 +--- a/include/winnt.h ++++ b/include/winnt.h +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #ifndef RC_INVOKED + #include diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93.myearlyrevert b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93.myearlyrevert index 01507070c..e7a70bbad 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93.myearlyrevert +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93.myearlyrevert @@ -7,9 +7,9 @@ index ef4a6d55558f..940106952141 100644 softpub.h \ spatialaudioclient.idl \ + specstrings.h \ + specstrings_strict.h \ sperror.h \ sql.h \ - sqlext.h \ diff --git a/include/ntdef.h b/include/ntdef.h index 68ced669baac..74c88ba572be 100644 --- a/include/ntdef.h diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/hotfixes index 214b03997..d42e509c6 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/hotfixes @@ -2,8 +2,10 @@ # Revert d4259ac8e9326a8173d6f83f0d525c97b90a1da0 which breaks compiling the steam helper if [ "$_EXTERNAL_INSTALL" = "proton" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9ae627316d98b3a0399bd6b5c2257bd47d596bc6 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 6bcdbbe5736f8c5f9adb1c4c687a3f23a4e5c261 HEAD ); then _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9ae627316d98b3a0399bd6b5c2257bd47d596bc6 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/mingw-msvcrt-steamhelper/d4259ac8e93-6bcdbbe) else _hotfix_mainlinereverts+=(d4259ac8e9326a8173d6f83f0d525c97b90a1da0) fi diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/oldtlb/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/oldtlb/hotfixes index ab679fc29..1f8260930 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/oldtlb/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/oldtlb/hotfixes @@ -2,7 +2,8 @@ # Revert data-only mode switch for usp10.dll tzres.dll stdole2.tlb shdoclc.dll sfc.dll security.dll normaliz.dll msimsg.dll mshtml.tib mferror.dll lz32.dll light.msstyles icmp.dll activeds.tlb # Else they are not getting loaded in Proton -if [ "$_EXTERNAL_INSTALL" = "proton" ]; then +# f1ff317 is a breaking point. Needs testing. +if [ "$_EXTERNAL_INSTALL" = "proton" ] && ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor f1ff3179aaf3beffcfcfc556ba03a09f29e30c34 HEAD ); then warning "Hotfix: Revert data-only mode switch for various dlls so they get properly loaded in Proton" _hotfix_mainlinereverts+=(000b637fa0d4e50c5534aa8520ac625b054cdf07 141be028802f1675366802d49af01982525c2e6d d5fc074b9f2cf2e52711d832ca76eaaa5277bb8c 457c5df7d33144e45e0b275cf3cd060ec8403f32 5b7534e55adb59cddb7f0c8a337cc3c3954c8d8b aa957a2db15942260864c50865f828adeccc12e8 2abcdf08033334075a22e65b97a7f8874361e72a 40611a65e73eee2ff8ff8ff647572f93a7ffd4ba 9b6253199ffb361557c53b1315263518cebc9871 d3e2fa064f2efe0a9375df23ec141171b74efe40 1bb2d490f79743e9dac87d279e15f29bd359e715 3584dd2900fbd3a11175d1b3f77a55315442c284 2da8b64cfd5ed46f98d1fbfa5d56b680358a7a6b 91db4290caa0bc4f0173e72296852de2d7ad699d ace84eb6bccc490a563af19118da9e19ede970bb 91544ee3bb6c7cd2c056ae0d0eb626ade701d09f) if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 000b637fa0d4e50c5534aa8520ac625b054cdf07 HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/proton_fs_hack_staging/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/proton_fs_hack_staging/hotfixes index dab902605..feb56ad5f 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/proton_fs_hack_staging/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/proton_fs_hack_staging/hotfixes @@ -190,7 +190,9 @@ EOM fi if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor dfd5f109fb4ebad859bf3ce3960b3b2b2ad1341d HEAD ); then _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Add_a_GPU_for_each_Vulkan_device_that_was_not_tied_to_an_XRandR_provider-legacy) - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Ignore_ClipCursor_if_desktop_window_is_foreground) + if ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Ignore_ClipCursor_if_desktop_window_is_foreground) + fi fi # /Legacy @@ -276,7 +278,9 @@ elif [ "$_proton_fs_hack" != "true" ] && [ "$_use_staging" = "true" ]; then if [ "$_protonify" = "true" ] && [ "$_childwindow_fix" = "true" ]; then if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor c86955d3806879fc97b127730e9fb90e232710a7 HEAD ); then _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Add_a_GPU_for_each_Vulkan_device_that_was_not_tied_to_an_XRandR_provider) - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Ignore_ClipCursor_if_desktop_window_is_foreground) + if ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Ignore_ClipCursor_if_desktop_window_is_foreground) + fi elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ef8e4b7e3e32e2beb317411c5bd6e5cedf71cfb7 HEAD ); then _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Add_a_GPU_for_each_Vulkan_device_that_was_not_tied_to_an_XRandR_provider-c86955d) _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/proton_fs_hack_staging/winex11.drv_Ignore_ClipCursor_if_desktop_window_is_foreground-c86955d) diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes6.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes6.mypatch new file mode 100644 index 000000000..fe6413388 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes6.mypatch @@ -0,0 +1,1612 @@ +From 29fb0539c5c1fb065311a73a12fe5500c84c3bc4 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 7 Dec 2020 12:59:55 +0300 +Subject: [PATCH] bcrypt: Implement DH. + +--- + dlls/bcrypt/bcrypt_internal.h | 18 ++ + dlls/bcrypt/bcrypt_main.c | 126 +++++++++++- + dlls/bcrypt/gnutls.c | 359 +++++++++++++++++++++++++++++++++- + include/bcrypt.h | 29 +++ + 4 files changed, 522 insertions(+), 10 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h +index 98223e90ac6..f0eaa6ad4fb 100644 +--- a/dlls/bcrypt/bcrypt_internal.h ++++ b/dlls/bcrypt/bcrypt_internal.h +@@ -131,6 +131,7 @@ enum alg_id + ALG_ID_RSA, + + /* secret agreement */ ++ ALG_ID_DH, + ALG_ID_ECDH_P256, + ALG_ID_ECDH_P384, + +@@ -173,6 +174,8 @@ struct key_symmetric + }; + + #define KEY_FLAG_LEGACY_DSA_V2 0x00000001 ++#define KEY_FLAG_DH_PARAMS_SET 0x00000002 ++#define KEY_FLAG_FINALIZED 0x00000004 + + struct key_asymmetric + { +@@ -196,6 +199,8 @@ struct key + struct secret + { + struct object hdr; ++ UCHAR *data; ++ ULONG data_len; + }; + + struct key_symmetric_set_auth_data_params +@@ -281,6 +286,9 @@ struct key_asymmetric_verify_params + + #define KEY_EXPORT_FLAG_PUBLIC 0x00000001 + #define KEY_EXPORT_FLAG_RSA_FULL 0x00000002 ++#define KEY_EXPORT_FLAG_DH_FULL 0x00000004 ++#define KEY_EXPORT_FLAG_DH_PARAMETERS 0x00000008 ++ + struct key_asymmetric_export_params + { + struct key *key; +@@ -291,6 +299,8 @@ struct key_asymmetric_export_params + }; + + #define KEY_IMPORT_FLAG_PUBLIC 0x00000001 ++#define KEY_IMPORT_FLAG_DH_FULL 0x00000004 ++#define KEY_IMPORT_FLAG_DH_PARAMETERS 0x00000008 + struct key_asymmetric_import_params + { + struct key *key; +@@ -299,6 +309,13 @@ struct key_asymmetric_import_params + ULONG len; + }; + ++struct key_secret_agreement_params ++{ ++ struct key *privkey; ++ struct key *pubkey; ++ struct secret *secret; ++}; ++ + enum key_funcs + { + unix_process_attach, +@@ -318,6 +335,7 @@ enum key_funcs + unix_key_asymmetric_destroy, + unix_key_asymmetric_export, + unix_key_asymmetric_import, ++ unix_key_secret_agreement, + }; + + #endif /* __BCRYPT_INTERNAL_H */ +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index cf05e8b4d44..72a7b1915f2 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -112,6 +112,7 @@ builtin_algorithms[] = + { BCRYPT_MD4_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 512 }, + { BCRYPT_MD2_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 128 }, + { BCRYPT_RSA_ALGORITHM, BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE, 0, 0, 0 }, ++ { BCRYPT_DH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P256_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P384_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_RSA_SIGN_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, +@@ -870,6 +871,20 @@ static NTSTATUS get_hash_property( const struct hash *hash, const WCHAR *prop, U + return status; + } + ++static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) ++{ ++ struct key_asymmetric_export_params params; ++ ++ if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED; ++ ++ params.key = (struct key *)key; ++ params.flags = KEY_EXPORT_FLAG_DH_PARAMETERS; ++ params.buf = buf; ++ params.len = size; ++ params.ret_len = ret_size; ++ return UNIX_CALL( key_asymmetric_export, ¶ms ); ++} ++ + static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) + { + switch (key->alg_id) +@@ -881,6 +896,9 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA + if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED; + return get_aes_property( key->u.s.mode, prop, buf, size, ret_size ); + ++ case ALG_ID_DH: ++ return get_dh_property( key, prop, buf, size, ret_size ); ++ + default: + FIXME( "unsupported algorithm %u\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -1130,6 +1148,8 @@ static NTSTATUS key_asymmetric_create( enum alg_id alg_id, ULONG bitlen, struct + return STATUS_NOT_IMPLEMENTED; + } + ++ if (alg_id == ALG_ID_DH && bitlen < 512) return STATUS_INVALID_PARAMETER; ++ + if (!(key = calloc( 1, sizeof(*key) ))) return STATUS_NO_MEMORY; + key->hdr.magic = MAGIC_KEY; + key->alg_id = alg_id; +@@ -1269,6 +1289,7 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY + static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size ) + { + struct key_asymmetric_export_params params; ++ BOOL dh_private = FALSE; + + if (!wcscmp( type, BCRYPT_KEY_DATA_BLOB )) + { +@@ -1328,6 +1349,15 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U + params.ret_len = size; + return UNIX_CALL( key_asymmetric_export, ¶ms ); + } ++ else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ) || (dh_private = !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))) ++ { ++ params.key = key; ++ params.flags = dh_private ? KEY_EXPORT_FLAG_DH_FULL : 0; ++ params.buf = output; ++ params.len = output_len; ++ params.ret_len = size; ++ return UNIX_CALL( key_asymmetric_export, ¶ms ); ++ } + + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_IMPLEMENTED; +@@ -1529,7 +1559,6 @@ static void key_destroy( struct key *key ) + } + else + UNIX_CALL( key_asymmetric_destroy, key ); +- + destroy_object( &key->hdr ); + } + +@@ -1537,6 +1566,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + ULONG input_len ) + { + struct key_asymmetric_import_params params; ++ BOOL dh_private = FALSE; + struct key *key; + NTSTATUS status; + ULONG size; +@@ -1591,6 +1621,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) +@@ -1638,6 +1669,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB )) +@@ -1663,6 +1695,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) +@@ -1685,6 +1718,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB )) +@@ -1707,6 +1741,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB )) +@@ -1747,6 +1782,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB )) /* not supported on native */ +@@ -1783,6 +1819,41 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; ++ return STATUS_SUCCESS; ++ } ++ else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ) || (dh_private = !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))) ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)input; ++ ULONG size; ++ ++ if (alg->id != ALG_ID_DH) return STATUS_NOT_SUPPORTED; ++ if (h->dwMagic != (dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC)) ++ { ++ WARN("unexpected dwMagic %#lx.\n", h->dwMagic); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ size = sizeof(*h) + h->cbKey * 3; ++ if (dh_private) ++ size += h->cbKey; ++ if (input_len != size) return STATUS_INVALID_PARAMETER; ++ if (h->cbKey * 8 < 512) return STATUS_INVALID_PARAMETER; ++ ++ if ((status = key_asymmetric_create( alg->id, h->cbKey * 8, &key ))) return status; ++ ++ params.key = key; ++ params.flags = dh_private ? KEY_IMPORT_FLAG_DH_FULL : 0; ++ params.buf = input; ++ params.len = input_len; ++ if ((status = UNIX_CALL( key_asymmetric_import, ¶ms ))) ++ { ++ key_destroy( key ); ++ return status; ++ } ++ ++ *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + +@@ -1828,11 +1899,15 @@ NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HAND + NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags ) + { + struct key *key = get_key_object( handle ); ++ NTSTATUS ret; + + TRACE( "%p, %#lx\n", key, flags ); + + if (!key) return STATUS_INVALID_HANDLE; +- return UNIX_CALL( key_asymmetric_generate, key ); ++ if (!(ret = UNIX_CALL( key_asymmetric_generate, key ))) ++ key->u.a.flags |= KEY_FLAG_FINALIZED; ++ ++ return ret; + } + + NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key, const WCHAR *type, +@@ -2146,6 +2221,21 @@ NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHA + case MAGIC_KEY: + { + struct key *key = (struct key *)object; ++ ++ if (key->alg_id == ALG_ID_DH) ++ { ++ if (!lstrcmpW( prop, BCRYPT_DH_PARAMETERS )) ++ { ++ struct key_asymmetric_import_params params; ++ ++ params.key = key; ++ params.flags = KEY_IMPORT_FLAG_DH_PARAMETERS; ++ params.buf = value; ++ params.len = size; ++ return UNIX_CALL( key_asymmetric_import, ¶ms ); ++ } ++ return STATUS_NOT_IMPLEMENTED; ++ } + return set_key_property( key, prop, value, size, flags ); + } + default: +@@ -2308,30 +2398,54 @@ NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULO + NTSTATUS WINAPI BCryptSecretAgreement( BCRYPT_KEY_HANDLE privkey_handle, BCRYPT_KEY_HANDLE pubkey_handle, + BCRYPT_SECRET_HANDLE *ret_handle, ULONG flags ) + { ++ struct key_secret_agreement_params params; + struct key *privkey = get_key_object( privkey_handle ); + struct key *pubkey = get_key_object( pubkey_handle ); + struct secret *secret; ++ NTSTATUS status; + + FIXME( "%p, %p, %p, %#lx\n", privkey_handle, pubkey_handle, ret_handle, flags ); + + if (!privkey || !pubkey) return STATUS_INVALID_HANDLE; + if (!is_agreement_key( privkey ) || !is_agreement_key( pubkey )) return STATUS_NOT_SUPPORTED; + if (!ret_handle) return STATUS_INVALID_PARAMETER; ++ if (is_symmetric_key( privkey ) || privkey->alg_id != pubkey->alg_id) return STATUS_INVALID_PARAMETER; ++ if (!(privkey->u.a.flags & pubkey->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_PARAMETER; ++ if (privkey->u.a.bitlen != pubkey->u.a.bitlen) return STATUS_INVALID_PARAMETER; + + if (!(secret = calloc( 1, sizeof(*secret) ))) return STATUS_NO_MEMORY; +- secret->hdr.magic = MAGIC_SECRET; ++ if (!(secret->data = malloc( privkey->u.a.bitlen / 8 ))) ++ { ++ free( secret ); ++ return STATUS_NO_MEMORY; ++ } + +- *ret_handle = secret; +- return STATUS_SUCCESS; ++ params.privkey = privkey; ++ params.pubkey = pubkey; ++ params.secret = secret; ++ ++ if ((status = UNIX_CALL( key_secret_agreement, ¶ms ))) ++ { ++ free( secret->data ); ++ free( secret ); ++ } ++ else ++ { ++ secret->hdr.magic = MAGIC_SECRET; ++ *ret_handle = secret; ++ } ++ return status; + } + + NTSTATUS WINAPI BCryptDestroySecret( BCRYPT_SECRET_HANDLE handle ) + { + struct secret *secret = get_secret_object( handle ); + +- FIXME( "%p\n", handle ); ++ TRACE( "%p\n", handle ); + + if (!secret) return STATUS_INVALID_HANDLE; ++ secret->hdr.magic = 0; ++ free( secret->data ); + destroy_object( &secret->hdr ); + return STATUS_SUCCESS; + } +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 89e5c91776d..4a9f223a838 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -79,6 +79,12 @@ typedef enum + typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t; + #endif + ++struct dh_key_data ++{ ++ UCHAR *pubkey; /* Used for DH private key only. */ ++ UCHAR *privkey; /* Used for DH private key only. */ ++}; ++ + union key_data + { + gnutls_cipher_hd_t cipher; +@@ -87,6 +93,11 @@ union key_data + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + } a; ++ struct /* DH */ ++ { ++ UCHAR *privkey; ++ UCHAR *pubkey; ++ } d; + }; + C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) ); + +@@ -95,6 +106,29 @@ static union key_data *key_data( struct key *key ) + return (union key_data *)key->private; + } + ++static unsigned int dh_pubkey_len( struct key *key ) ++{ ++ return sizeof(BCRYPT_DH_KEY_BLOB) + key->u.a.bitlen / 8 * 3; ++} ++ ++static void dh_key_free( struct key *key ) ++{ ++ free( key_data(key)->d.privkey ); ++ free( key_data(key)->d.pubkey ); ++} ++ ++static void dh_key_alloc( struct key *key ) ++{ ++ unsigned int bitlen = key->u.a.bitlen; ++ ++ if (key_data(key)->d.pubkey) return; ++ ++ key_data(key)->d.privkey = calloc( 1, bitlen / 8 ); ++ key_data(key)->d.pubkey = calloc( 1, dh_pubkey_len( key )); ++} ++ ++static BOOL dh_supported; ++ + /* Not present in gnutls version < 3.0 */ + static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t); + static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); +@@ -144,6 +178,17 @@ static void (*pgnutls_x509_spki_set_rsa_pss_params)(gnutls_x509_spki_t, gnutls_d + static int (*pgnutls_pubkey_set_spki)(gnutls_pubkey_t, const gnutls_x509_spki_t, unsigned int); + static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_t, unsigned int); + ++static int (*pgnutls_dh_params_init)(gnutls_dh_params_t * dh_params); ++static void (*pgnutls_dh_params_deinit)(gnutls_dh_params_t dh_params); ++static int (*pgnutls_dh_params_generate2)(gnutls_dh_params_t dparams, unsigned int bits); ++static int (*pgnutls_dh_params_import_raw2)(gnutls_dh_params_t dh_params, const gnutls_datum_t * prime, ++ const gnutls_datum_t * generator, unsigned key_bits); ++static int (*pgnutls_dh_params_export_raw)(gnutls_dh_params_t params, gnutls_datum_t * prime, ++ gnutls_datum_t * generator, unsigned int *bits); ++static int (*pgnutls_dh_generate_key)(gnutls_dh_params_t dh_params, gnutls_datum_t *priv_key, gnutls_datum_t *pub_key); ++static int (*pgnutls_dh_compute_key)(gnutls_dh_params_t dh_params, const gnutls_datum_t *priv_key, ++ const gnutls_datum_t *pub_key, const gnutls_datum_t *peer_key, gnutls_datum_t *Z); ++ + static void *libgnutls_handle; + #define MAKE_FUNCPTR(f) static typeof(f) * p##f + MAKE_FUNCPTR(gnutls_cipher_decrypt2); +@@ -392,6 +437,39 @@ static NTSTATUS gnutls_process_attach( void *args ) + pgnutls_perror( ret ); + goto fail; + } ++ if (!(pgnutls_dh_params_init = dlsym( libgnutls_handle, "gnutls_dh_params_init" ))) ++ { ++ WARN("gnutls_dh_params_init not found\n"); ++ } ++ if (!(pgnutls_dh_params_deinit = dlsym( libgnutls_handle, "gnutls_dh_params_deinit" ))) ++ { ++ WARN("gnutls_dh_params_deinit not found\n"); ++ } ++ if (!(pgnutls_dh_params_generate2 = dlsym( libgnutls_handle, "gnutls_dh_params_generate2" ))) ++ { ++ WARN("gnutls_dh_params_generate2 not found\n"); ++ } ++ if (!(pgnutls_dh_params_import_raw2 = dlsym( libgnutls_handle, "gnutls_dh_params_import_raw2" ))) ++ { ++ WARN("gnutls_dh_params_import_raw2 not found\n"); ++ } ++ if (!(pgnutls_dh_params_export_raw = dlsym( libgnutls_handle, "gnutls_dh_params_export_raw" ))) ++ { ++ WARN("gnutls_dh_params_export_raw not found\n"); ++ } ++ if (!(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "_gnutls_dh_generate_key" )) ++ && !(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "gnutls_dh_generate_key" ))) ++ { ++ WARN("gnutls_dh_generate_key not found\n"); ++ } ++ if (!(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "_gnutls_dh_compute_key" )) ++ && !(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "gnutls_dh_compute_key" ))) ++ { ++ WARN("gnutls_dh_compute_key not found\n"); ++ } ++ ++ dh_supported = pgnutls_dh_params_init && pgnutls_dh_params_generate2 && pgnutls_dh_params_import_raw2 ++ && pgnutls_dh_generate_key && pgnutls_dh_compute_key; + + if (TRACE_ON( bcrypt )) + { +@@ -951,6 +1029,89 @@ static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l + return status; + } + ++static NTSTATUS CDECL key_dh_generate( struct key *key ) ++{ ++ gnutls_datum_t prime, generator, privkey, pubkey; ++ NTSTATUS status = STATUS_SUCCESS; ++ gnutls_dh_params_t dh_params; ++ ULONG key_length; ++ int ret; ++ ++ if (!dh_supported) ++ { ++ ERR("DH is not available.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ key_length = key->u.a.bitlen / 8; ++ ++ if (!(key->u.a.flags & KEY_FLAG_DH_PARAMS_SET)) ++ { ++ /* Generate parameters, export and then import them back below. ++ * The bitlen in dh parameters (which is later used for keys generation) ++ * is not set to gnutls_dh_params_generate2 'bits' parameter as one ++ * could expect. gnutls_dh_params_generate2 generates 'q' (which is not ++ * actually needed for DH) with the estimated bit length and then ++ * sets the bit length to the 'q' bitlength. */ ++ dh_key_alloc( key ); ++ ++ if ((ret = pgnutls_dh_params_generate2( dh_params, key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ if ((ret = pgnutls_dh_params_export_raw( dh_params, &prime, &generator, NULL ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1), key_length, &prime, 1 ); ++ export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, ++ key_length, &generator, 1 ); ++ free( prime.data ); ++ free( generator.data ); ++ ++ key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; ++ } ++ ++ prime.size = generator.size = key_length; ++ prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1); ++ generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length; ++ ++ if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if ((ret = pgnutls_dh_generate_key( dh_params, &privkey, &pubkey ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ export_gnutls_datum( (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, ++ key_length, &pubkey, 1 ); ++ export_gnutls_datum( key_data(key)->d.privkey, key_length, &privkey, 1); ++ ++ free( privkey.data ); ++ free( pubkey.data ); ++ pgnutls_dh_params_deinit( dh_params ); ++ ++ return status; ++} ++ + static NTSTATUS key_asymmetric_generate( void *args ) + { + struct key *key = args; +@@ -961,7 +1122,7 @@ static NTSTATUS key_asymmetric_generate( void *args ) + int ret; + + if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; +- if (key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; ++ if (key->alg_id != ALG_ID_DH && key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; + + switch (key->alg_id) + { +@@ -988,6 +1149,9 @@ static NTSTATUS key_asymmetric_generate( void *args ) + bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 ); + break; + ++ case ALG_ID_DH: ++ return key_dh_generate( key ); ++ + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return STATUS_NOT_SUPPORTED; +@@ -1000,6 +1164,7 @@ static NTSTATUS key_asymmetric_generate( void *args ) + } + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { ++ ERR("gnutls error bitlen %u.\n", bitlen); + pgnutls_perror( ret ); + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; +@@ -1557,6 +1722,44 @@ static NTSTATUS key_asymmetric_export( void *args ) + return key_export_dsa_capi( key, params->buf, params->len, params->ret_len ); + return STATUS_NOT_IMPLEMENTED; + ++ case ALG_ID_DH: ++ if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; ++ if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS) ++ { ++ BCRYPT_DH_PARAMETER_HEADER *h; ++ unsigned int data_size; ++ ++ data_size = sizeof(BCRYPT_DH_PARAMETER_HEADER) + key->u.a.bitlen / 8 * 2; ++ if (params->ret_len) *params->ret_len = data_size; ++ if (!params->buf) return STATUS_SUCCESS; ++ if (params->len < data_size) return STATUS_BUFFER_TOO_SMALL; ++ ++ h = (BCRYPT_DH_PARAMETER_HEADER *)params->buf; ++ h->cbLength = data_size; ++ h->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; ++ h->cbKeyLength = key->u.a.bitlen / 8; ++ memcpy( h + 1, (BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, h->cbKeyLength * 2); ++ } ++ else ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)params->buf; ++ BOOL dh_private = flags & KEY_EXPORT_FLAG_DH_FULL; ++ ++ *params->ret_len = dh_pubkey_len( key ); ++ if (dh_private) ++ *params->ret_len += key->u.a.bitlen / 8; ++ ++ if (params->len < *params->ret_len) return STATUS_SUCCESS; ++ ++ memcpy(params->buf, key_data(key)->d.pubkey, dh_pubkey_len( key )); ++ if (dh_private) ++ memcpy(params->buf + dh_pubkey_len( key ), key_data(key)->d.privkey, key->u.a.bitlen / 8); ++ ++ h->dwMagic = dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC; ++ h->cbKey = key->u.a.bitlen / 8; ++ } ++ return STATUS_SUCCESS; ++ + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -1604,6 +1807,49 @@ static NTSTATUS key_asymmetric_import( void *args ) + FIXME( "DSA private key not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + ++ case ALG_ID_DH: ++ if (flags & KEY_IMPORT_FLAG_DH_PARAMETERS) ++ { ++ const BCRYPT_DH_PARAMETER_HEADER *h = (const BCRYPT_DH_PARAMETER_HEADER *)params->buf; ++ ULONG param_size = sizeof(BCRYPT_DH_PARAMETER_HEADER) + key->u.a.bitlen / 8 * 2; ++ ++ if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; ++ if (params->len < param_size) return STATUS_BUFFER_TOO_SMALL; ++ if (!h || h->cbLength != param_size || h->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC ++ || h->cbKeyLength != key->u.a.bitlen / 8) ++ return STATUS_INVALID_PARAMETER; ++ ++ dh_key_alloc( key ); ++ memcpy((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, h + 1, h->cbKeyLength * 2); ++ key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; ++ } ++ else ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)params->buf; ++ BOOL dh_private = flags & KEY_IMPORT_FLAG_DH_FULL; ++ ULONG size; ++ ++ if (h->dwMagic != (dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC)) ++ { ++ WARN("unexpected dwMagic %#x.\n", (int)h->dwMagic); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ size = sizeof(*h) + h->cbKey * 3; ++ if (dh_private) ++ size += h->cbKey; ++ if (params->len != size) return STATUS_INVALID_PARAMETER; ++ if (h->cbKey * 8 < 512) return STATUS_INVALID_PARAMETER; ++ ++ dh_key_alloc( key ); ++ ++ memcpy( key_data(key)->d.pubkey, params->buf, dh_pubkey_len( key )); ++ ++ if (dh_private) ++ memcpy( key_data(key)->d.privkey, params->buf + sizeof(*h) + h->cbKey * 3, h->cbKey); ++ } ++ return STATUS_SUCCESS; ++ + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -2018,8 +2264,15 @@ static NTSTATUS key_asymmetric_destroy( void *args ) + { + struct key *key = args; + +- if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); +- if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); ++ if (key->alg_id == ALG_ID_DH) ++ { ++ dh_key_free( key ); ++ } ++ else ++ { ++ if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); ++ if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); ++ } + return STATUS_SUCCESS; + } + +@@ -2094,6 +2347,7 @@ static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy ) + } + break; + } ++ + default: + ERR( "unhandled algorithm %u\n", key_orig->alg_id ); + return STATUS_INTERNAL_ERROR; +@@ -2187,6 +2441,19 @@ static NTSTATUS key_asymmetric_duplicate( void *args ) + const struct key_asymmetric_duplicate_params *params = args; + NTSTATUS status; + ++ if (params->key_orig->alg_id == ALG_ID_DH) ++ { ++ union key_data *s = key_data( params->key_orig ); ++ union key_data *d = key_data( params->key_copy ); ++ if (s->d.privkey) ++ { ++ dh_key_alloc( params->key_copy ); ++ memcpy( d->d.privkey, s->d.privkey, params->key_orig->u.a.bitlen / 8 ); ++ memcpy( d->d.pubkey, s->d.pubkey, dh_pubkey_len( params->key_orig )); ++ } ++ return STATUS_SUCCESS; ++ } ++ + if (key_data(params->key_orig)->a.privkey && (status = dup_privkey( params->key_orig, params->key_copy ))) + return status; + +@@ -2245,6 +2512,89 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) + return status; + } + ++static NTSTATUS key_secret_agreement( void *args ) ++{ ++ struct key_secret_agreement_params *params = args; ++ struct secret *secret; ++ struct key *priv_key; ++ struct key *peer_key; ++ int ret; ++ ++ priv_key = params->privkey; ++ peer_key = params->pubkey; ++ secret = params->secret; ++ ++ switch (priv_key->alg_id) ++ { ++ case ALG_ID_DH: ++ { ++ gnutls_datum_t prime, generator, priv, peer, secret_datum; ++ gnutls_dh_params_t dh_params; ++ ULONG key_length; ++ ++ if (!dh_supported) ++ { ++ ERR("DH is not available.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ key_length = priv_key->u.a.bitlen / 8; ++ ++ prime.size = generator.size = key_length; ++ prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1); ++ generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1) + key_length; ++ ++ if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, priv_key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ priv.size = peer.size = key_length; ++ priv.data = key_data(priv_key)->d.privkey; ++ peer.data = key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2; ++ ++ if (memcmp((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, ++ key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB), key_length * 2)) ++ { ++ ERR("peer DH paramaters do not match.\n"); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if ((ret = pgnutls_dh_compute_key( dh_params, &priv, NULL, &peer, &secret_datum ))) ++ { ++ ERR("Error computing shared key.\n"); ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ TRACE("secret_datum.size %u, key_length %u.\n", secret_datum.size, (int)key_length); ++ export_gnutls_datum( secret->data, key_length, &secret_datum, 1 ); ++ secret->data_len = key_length; ++ free( secret_datum.data ); ++ break; ++ } ++ ++ case ALG_ID_ECDH_P256: ++ FIXME("ECDH is not supported.\n"); ++ break; ++ ++ default: ++ ERR( "unhandled algorithm %u\n", priv_key->alg_id ); ++ return STATUS_INVALID_HANDLE; ++ } ++ return STATUS_SUCCESS; ++} ++ + const unixlib_entry_t __wine_unix_call_funcs[] = + { + gnutls_process_attach, +@@ -2263,7 +2613,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + key_asymmetric_verify, + key_asymmetric_destroy, + key_asymmetric_export, +- key_asymmetric_import ++ key_asymmetric_import, ++ key_secret_agreement, + }; + + #ifdef _WIN64 +diff --git a/include/bcrypt.h b/include/bcrypt.h +index 6822491ed36..34c79d9cbf4 100644 +--- a/include/bcrypt.h ++++ b/include/bcrypt.h +@@ -62,6 +62,8 @@ typedef LONG NTSTATUS; + #define BCRYPT_OPAQUE_KEY_BLOB L"OpaqueKeyBlob" + #define BCRYPT_KEY_DATA_BLOB L"KeyDataBlob" + #define BCRYPT_AES_WRAP_KEY_BLOB L"Rfc3565KeyWrapBlob" ++#define BCRYPT_DH_PUBLIC_BLOB L"DHPUBLICBLOB" ++#define BCRYPT_DH_PRIVATE_BLOB L"DHPRIVATEBLOB" + #define BCRYPT_ECCPUBLIC_BLOB L"ECCPUBLICBLOB" + #define BCRYPT_ECCPRIVATE_BLOB L"ECCPRIVATEBLOB" + #define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB" +@@ -82,6 +84,7 @@ typedef LONG NTSTATUS; + #define BCRYPT_3DES_ALGORITHM L"3DES" + #define BCRYPT_AES_ALGORITHM L"AES" + #define BCRYPT_DES_ALGORITHM L"DES" ++#define BCRYPT_DH_ALGORITHM L"DH" + #define BCRYPT_DSA_ALGORITHM L"DSA" + #define BCRYPT_ECDH_P256_ALGORITHM L"ECDH_P256" + #define BCRYPT_ECDH_P384_ALGORITHM L"ECDH_P384" +@@ -113,6 +116,8 @@ typedef LONG NTSTATUS; + #define BCRYPT_KDF_TLS_PRF L"TLS_PRF" + #define BCRYPT_KDF_SP80056A_CONCAT L"SP800_56A_CONCAT" + #define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" ++ ++#define BCRYPT_DH_PARAMETERS L"DHParameters" + #else + static const WCHAR BCRYPT_ALGORITHM_NAME[] = {'A','l','g','o','r','i','t','h','m','N','a','m','e',0}; + static const WCHAR BCRYPT_AUTH_TAG_LENGTH[] = {'A','u','t','h','T','a','g','L','e','n','g','t','h',0}; +@@ -135,6 +140,8 @@ static const WCHAR BCRYPT_SIGNATURE_LENGTH[] = {'S','i','g','n','a','t','u','r', + static const WCHAR BCRYPT_OPAQUE_KEY_BLOB[] = {'O','p','a','q','u','e','K','e','y','B','l','o','b',0}; + static const WCHAR BCRYPT_KEY_DATA_BLOB[] = {'K','e','y','D','a','t','a','B','l','o','b',0}; + static const WCHAR BCRYPT_AES_WRAP_KEY_BLOB[] = {'R','f','c','3','5','6','5','K','e','y','W','r','a','p','B','l','o','b',0}; ++static const WCHAR BCRYPT_DH_PUBLIC_BLOB[] = {'D','H','P','U','B','L','I','C','B','L','O','B',0}; ++static const WCHAR BCRYPT_DH_PRIVATE_BLOB[] = {'D','H','P','R','I','V','A','T','E','B','L','O','B',0}; + static const WCHAR BCRYPT_ECCPUBLIC_BLOB[] = {'E','C','C','P','U','B','L','I','C','B','L','O','B',0}; + static const WCHAR BCRYPT_ECCPRIVATE_BLOB[] = {'E','C','C','P','R','I','V','A','T','E','B','L','O','B',0}; + static const WCHAR BCRYPT_RSAPUBLIC_BLOB[] = {'R','S','A','P','U','B','L','I','C','B','L','O','B',0}; +@@ -157,6 +164,7 @@ static const WCHAR MS_PLATFORM_CRYPTO_PROVIDER[] = \ + static const WCHAR BCRYPT_3DES_ALGORITHM[] = {'3','D','E','S',0}; + static const WCHAR BCRYPT_AES_ALGORITHM[] = {'A','E','S',0}; + static const WCHAR BCRYPT_DES_ALGORITHM[] = {'D','E','S',0}; ++static const WCHAR BCRYPT_DH_ALGORITHM[] = {'D','H',0}; + static const WCHAR BCRYPT_DSA_ALGORITHM[] = {'D','S','A',0}; + static const WCHAR BCRYPT_ECDH_P256_ALGORITHM[] = {'E','C','D','H','_','P','2','5','6',0}; + static const WCHAR BCRYPT_ECDH_P384_ALGORITHM[] = {'E','C','D','H','_','P','3','8','4',0}; +@@ -188,6 +196,7 @@ static const WCHAR BCRYPT_KDF_HMAC[] = {'H','M','A','C',0}; + static const WCHAR BCRYPT_KDF_TLS_PRF[] = {'T','L','S','_','P','R','F',0}; + static const WCHAR BCRYPT_KDF_SP80056A_CONCAT[] = {'S','P','8','0','0','_','5','6','A','_','C','O','N','C','A','T',0}; + static const WCHAR BCRYPT_KDF_RAW_SECRET[] = {'T','R','U','N','C','A','T','E',0}; ++#define BCRYPT_DH_PARAMETERS u"DHParameters" + #endif + + #define BCRYPT_ECDSA_PUBLIC_P256_MAGIC 0x31534345 +@@ -320,6 +329,15 @@ typedef struct _BCRYPT_DSA_KEY_BLOB + #define BCRYPT_DSA_PUBLIC_MAGIC_V2 0x32425044 + #define BCRYPT_DSA_PRIVATE_MAGIC_V2 0x32565044 + ++typedef struct _BCRYPT_DH_KEY_BLOB ++{ ++ ULONG dwMagic; ++ ULONG cbKey; ++} BCRYPT_DH_KEY_BLOB, *PBCRYPT_DH_KEY_BLOB; ++ ++#define BCRYPT_DH_PUBLIC_MAGIC 0x42504844 ++#define BCRYPT_DH_PRIVATE_MAGIC 0x56504844 ++ + typedef enum + { + DSA_HASH_ALGORITHM_SHA1, +@@ -379,6 +397,15 @@ typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER + ULONG cbKeyData; + } BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER; + ++typedef struct _BCRYPT_DH_PARAMETER_HEADER ++{ ++ ULONG cbLength; ++ ULONG dwMagic; ++ ULONG cbKeyLength; ++} BCRYPT_DH_PARAMETER_HEADER; ++ ++#define BCRYPT_DH_PARAMETERS_MAGIC 0x4d504844 ++ + #define KDF_HASH_ALGORITHM 0x00000000 + #define KDF_SECRET_PREPEND 0x00000001 + #define KDF_SECRET_APPEND 0x00000002 +@@ -408,6 +435,8 @@ typedef PVOID BCRYPT_HANDLE; + typedef PVOID BCRYPT_HASH_HANDLE; + typedef PVOID BCRYPT_SECRET_HANDLE; + ++#define BCRYPT_NO_KEY_VALIDATION 0x00000008 ++ + /* Pseudo handles */ + #define BCRYPT_MD2_ALG_HANDLE ((BCRYPT_ALG_HANDLE)0x00000001) + #define BCRYPT_MD4_ALG_HANDLE ((BCRYPT_ALG_HANDLE)0x00000011) + +From f730ca784e1d027b674b65c3de0d5b04cf0930f1 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 9 Dec 2020 20:10:13 +0300 +Subject: [PATCH] bcrypt: Implement BCryptDeriveKey() for _KDF_RAW_SECRET. + +Extracted from the patch implementing ECDH on top of gcrypt by Derek Lesho. +--- + dlls/bcrypt/bcrypt_main.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index 72a7b1915f2..e6016a062c5 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -2455,12 +2455,35 @@ NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, + { + struct secret *secret = get_secret_object( handle ); + +- FIXME( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags ); ++ TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags ); + + if (!secret) return STATUS_INVALID_HANDLE; + if (!kdf) return STATUS_INVALID_PARAMETER; + +- return STATUS_INTERNAL_ERROR; ++ if (flags) FIXME("flags ignored: %#lx\n", flags); ++ ++ if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) ++ { ++ ULONG secret_length = secret->data_len; ++ unsigned int i;; ++ ++ if (!derived) ++ { ++ *result = secret_length; ++ return STATUS_SUCCESS; ++ } ++ ++ /* outputs in little endian for some reason */ ++ for (i = 0; i < min(secret_length, derived_size); i++) ++ { ++ derived[i] = secret->data[secret_length - i - 1]; ++ } ++ ++ *result = i; ++ return STATUS_SUCCESS; ++ } ++ FIXME( "Derivation function %s not supported.\n", debugstr_w(kdf) ); ++ return STATUS_NOT_IMPLEMENTED; + } + + BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) + +From ebc03fd79e25ca53582d14a7098b2cb454a10018 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Tue, 7 Jan 2020 14:22:49 -0600 +Subject: [PATCH] bcrypt: Implement BCRYPT_KDF_HASH. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47699 +Signed-off-by: Derek Lesho +--- + dlls/bcrypt/bcrypt_main.c | 107 ++++++++++++++++++++++++++++++++++++- + dlls/bcrypt/tests/bcrypt.c | 3 +- + 2 files changed, 107 insertions(+), 3 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index e6016a062c5..bcb7a692ffd 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -2462,7 +2462,112 @@ NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, + + if (flags) FIXME("flags ignored: %#lx\n", flags); + +- if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) ++ if (!(lstrcmpW( kdf, BCRYPT_KDF_HASH ))) ++ { ++ unsigned int i; ++ BCryptBuffer *hash_algorithm = NULL; ++ BCryptBuffer *secret_prepend = NULL; ++ BCryptBuffer *secret_append = NULL; ++ enum alg_id hash_alg_id; ++ ULONG hash_length; ++ struct hash_impl hash; ++ NTSTATUS status; ++ ++ if (parameter) ++ { ++ for (i = 0; i < parameter->cBuffers; i++) ++ { ++ BCryptBuffer *cur_buffer = ¶meter->pBuffers[i]; ++ switch(cur_buffer->BufferType) ++ { ++ case KDF_HASH_ALGORITHM: ++ if (hash_algorithm) ++ FIXME("Duplicate KDF_HASH_ALGORITHM, untested\n"); ++ hash_algorithm = cur_buffer; ++ break; ++ case KDF_SECRET_PREPEND: ++ if (secret_prepend) ++ FIXME("Multiple prefixes unsupported\n"); ++ secret_prepend = cur_buffer; ++ break; ++ case KDF_SECRET_APPEND: ++ if (secret_append) ++ FIXME("Multiple suffixes unsupported\n"); ++ secret_append = cur_buffer; ++ break; ++ default: ++ FIXME("Unsupported BCRYPT_KDF_HASH parameter type %#lx\n", cur_buffer->BufferType); ++ break; ++ } ++ } ++ } ++ ++ if (!(hash_algorithm)) ++ hash_alg_id = ALG_ID_SHA1; ++ else ++ { ++ for (i = 0; i < ARRAY_SIZE( builtin_algorithms ); i++) ++ { ++ if (!lstrcmpW( hash_algorithm->pvBuffer, builtin_algorithms[i].name)) ++ { ++ hash_alg_id = i; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(builtin_algorithms)) ++ { ++ WARN("Algorithm %s not found\n", debugstr_w(hash_algorithm->pvBuffer)); ++ return STATUS_NOT_SUPPORTED; ++ } ++ if (builtin_algorithms[hash_alg_id].class != BCRYPT_HASH_INTERFACE) ++ { ++ WARN("Incorrect class %lu\n", builtin_algorithms[hash_alg_id].class); ++ return STATUS_NOT_SUPPORTED; ++ } ++ } ++ ++ hash_length = builtin_algorithms[hash_alg_id].hash_length; ++ ++ if (!derived) ++ { ++ *result = hash_length; ++ return STATUS_SUCCESS; ++ } ++ ++ if ((status = hash_init(&hash, hash_alg_id))) ++ { ++ return status; ++ } ++ ++ if (secret_prepend) ++ { ++ hash_update(&hash, hash_alg_id, secret_prepend->pvBuffer, secret_prepend->cbBuffer); ++ } ++ ++ hash_update(&hash, hash_alg_id, secret->data, secret->data_len); ++ ++ if (secret_append) ++ { ++ hash_update(&hash, hash_alg_id, secret_append->pvBuffer, secret_append->cbBuffer); ++ } ++ ++ if (derived_size >= hash_length) ++ { ++ hash_finish(&hash, hash_alg_id, derived); ++ *result = hash_length; ++ } ++ else ++ { ++ UCHAR *output = malloc(hash_length); ++ hash_finish(&hash, hash_alg_id, output); ++ memcpy(derived, output, derived_size); ++ free(output); ++ *result = derived_size; ++ } ++ ++ return STATUS_SUCCESS; ++ } ++ else if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) + { + ULONG secret_length = secret->data_len; + unsigned int i;; + + +From b0c2492c38a33c37e8877a1572eb0679b948a96f Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Dec 2020 04:07:13 +0300 +Subject: [PATCH] bcrypt: Reimplement DH using libgmp instead of private gnutls + functions. + +--- + configure.ac | 8 ++ + dlls/bcrypt/Makefile.in | 2 +- + dlls/bcrypt/gnutls.c | 300 +++++++++++++++++++++++++++++----------- + 3 files changed, 228 insertions(+), 82 deletions(-) + +diff --git a/configure.ac b/configure.ac +index a7268c12d15..c9bc5897f91 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1419,6 +1419,14 @@ fi + WINE_WARNING_WITH(gnutls,[test "x$ac_cv_lib_soname_gnutls" = "x"], + [libgnutls ${notice_platform}development files not found, no schannel support.]) + ++dnl **** Check for libgmp **** ++if test "x$with_gnutls" != "xno" ++then ++ WINE_PACKAGE_FLAGS(GMP,[gmp],[-lgmp],,, ++ [AC_CHECK_HEADERS([gmp.h], ++ [WINE_CHECK_SONAME(gmp,__gmpz_init,,[GMP_CFLAGS=""],[$GMP_LIBS],[[libgmp-*]])])]) ++fi ++ + dnl **** Check for SANE **** + if test "x$with_sane" != "xno" + then +diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in +index 11f4acea4f2..b8531957f63 100644 +--- a/dlls/bcrypt/Makefile.in ++++ b/dlls/bcrypt/Makefile.in +@@ -2,7 +2,7 @@ MODULE = bcrypt.dll + IMPORTS = advapi32 + IMPORTLIB = bcrypt + UNIXLIB = bcrypt.so +-UNIX_CFLAGS = $(GNUTLS_CFLAGS) ++UNIX_CFLAGS = $(GNUTLS_CFLAGS) $(GMP_CFLAGS) + + C_SRCS = \ + bcrypt_main.c \ +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 4a9f223a838..d0cc55136e1 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -48,6 +48,15 @@ + + #include "wine/debug.h" + ++#include ++ ++#ifdef HAVE_GMP_H ++#include ++#endif ++ ++#include ++#include ++ + WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); + WINE_DECLARE_DEBUG_CHANNEL(winediag); + +@@ -127,8 +136,6 @@ static void dh_key_alloc( struct key *key ) + key_data(key)->d.pubkey = calloc( 1, dh_pubkey_len( key )); + } + +-static BOOL dh_supported; +- + /* Not present in gnutls version < 3.0 */ + static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t); + static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); +@@ -185,11 +192,9 @@ static int (*pgnutls_dh_params_import_raw2)(gnutls_dh_params_t dh_params, const + const gnutls_datum_t * generator, unsigned key_bits); + static int (*pgnutls_dh_params_export_raw)(gnutls_dh_params_t params, gnutls_datum_t * prime, + gnutls_datum_t * generator, unsigned int *bits); +-static int (*pgnutls_dh_generate_key)(gnutls_dh_params_t dh_params, gnutls_datum_t *priv_key, gnutls_datum_t *pub_key); +-static int (*pgnutls_dh_compute_key)(gnutls_dh_params_t dh_params, const gnutls_datum_t *priv_key, +- const gnutls_datum_t *pub_key, const gnutls_datum_t *peer_key, gnutls_datum_t *Z); + + static void *libgnutls_handle; ++ + #define MAKE_FUNCPTR(f) static typeof(f) * p##f + MAKE_FUNCPTR(gnutls_cipher_decrypt2); + MAKE_FUNCPTR(gnutls_cipher_deinit); +@@ -209,6 +214,22 @@ MAKE_FUNCPTR(gnutls_pubkey_deinit); + MAKE_FUNCPTR(gnutls_pubkey_encrypt_data); + MAKE_FUNCPTR(gnutls_pubkey_import_privkey); + MAKE_FUNCPTR(gnutls_pubkey_init); ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++static BOOL dh_supported; ++static void *libgmp_handle; ++ ++MAKE_FUNCPTR(mpz_init); ++MAKE_FUNCPTR(mpz_clear); ++MAKE_FUNCPTR(mpz_cmp); ++MAKE_FUNCPTR(_mpz_cmp_ui); ++MAKE_FUNCPTR(mpz_sizeinbase); ++MAKE_FUNCPTR(mpz_import); ++MAKE_FUNCPTR(mpz_export); ++MAKE_FUNCPTR(mpz_mod); ++MAKE_FUNCPTR(mpz_powm); ++MAKE_FUNCPTR(mpz_sub_ui); ++#endif + #undef MAKE_FUNCPTR + + static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size) +@@ -398,6 +419,37 @@ static NTSTATUS gnutls_process_attach( void *args ) + LOAD_FUNCPTR(gnutls_pubkey_init); + #undef LOAD_FUNCPTR + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++#define LOAD_FUNCPTR_STR(f) #f ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( libgmp_handle, LOAD_FUNCPTR_STR(f) ))) \ ++ { \ ++ ERR( "failed to load %s\n", LOAD_FUNCPTR_STR(f) ); \ ++ goto fail; \ ++ } ++ ++ if ((libgmp_handle = dlopen( SONAME_LIBGMP, RTLD_NOW ))) ++ { ++ LOAD_FUNCPTR(mpz_init); ++ LOAD_FUNCPTR(mpz_clear); ++ LOAD_FUNCPTR(mpz_cmp); ++ LOAD_FUNCPTR(_mpz_cmp_ui); ++ LOAD_FUNCPTR(mpz_sizeinbase); ++ LOAD_FUNCPTR(mpz_import); ++ LOAD_FUNCPTR(mpz_export); ++ LOAD_FUNCPTR(mpz_mod); ++ LOAD_FUNCPTR(mpz_powm); ++ LOAD_FUNCPTR(mpz_sub_ui); ++ } ++ else ++ { ++ ERR_(winediag)( "failed to load libgmp, no support for DH\n" ); ++ goto fail; ++ } ++#undef LOAD_FUNCPTR ++#undef LOAD_FUNCPTR_STR ++#endif ++ + #define LOAD_FUNCPTR_OPT(f) \ + if (!(p##f = dlsym( libgnutls_handle, #f ))) \ + { \ +@@ -457,19 +509,13 @@ static NTSTATUS gnutls_process_attach( void *args ) + { + WARN("gnutls_dh_params_export_raw not found\n"); + } +- if (!(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "_gnutls_dh_generate_key" )) +- && !(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "gnutls_dh_generate_key" ))) +- { +- WARN("gnutls_dh_generate_key not found\n"); +- } +- if (!(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "_gnutls_dh_compute_key" )) +- && !(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "gnutls_dh_compute_key" ))) +- { +- WARN("gnutls_dh_compute_key not found\n"); +- } + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) + dh_supported = pgnutls_dh_params_init && pgnutls_dh_params_generate2 && pgnutls_dh_params_import_raw2 +- && pgnutls_dh_generate_key && pgnutls_dh_compute_key; ++ && libgmp_handle; ++#else ++ ERR_(winediag)("Compiled without DH support.\n"); ++#endif + + if (TRACE_ON( bcrypt )) + { +@@ -482,6 +528,14 @@ static NTSTATUS gnutls_process_attach( void *args ) + fail: + dlclose( libgnutls_handle ); + libgnutls_handle = NULL; ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++ if (libgmp_handle) ++ { ++ dlclose( libgmp_handle ); ++ libgmp_handle = NULL; ++ } ++#endif + return STATUS_DLL_NOT_FOUND; + } + +@@ -494,6 +548,11 @@ static NTSTATUS gnutls_process_detach( void *args ) + libgnutls_handle = NULL; + } + return STATUS_SUCCESS; ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++ dlclose( libgmp_handle ); ++ libgmp_handle = NULL; ++#endif + } + + struct buffer +@@ -1029,12 +1088,61 @@ static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l + return status; + } + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++static NTSTATUS CDECL gen_random(void *buffer, unsigned int length) ++{ ++ unsigned int read_size; ++ int dev_random; ++ ++ dev_random = open("/dev/urandom", O_RDONLY); ++ if (dev_random == -1) ++ { ++ FIXME("couldn't open /dev/urandom.\n"); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ read_size = read(dev_random, buffer, length); ++ close(dev_random); ++ if (read_size != length) ++ { ++ FIXME("Could not read from /dev/urandom."); ++ return STATUS_INTERNAL_ERROR; ++ } ++ return STATUS_SUCCESS; ++} ++ ++static void import_mpz(mpz_t value, const void *input, unsigned int length) ++{ ++ pmpz_import(value, length, 1, 1, 0, 0, input); ++} ++ ++static void export_mpz(void *output, unsigned int length, const mpz_t value) ++{ ++ size_t export_length; ++ unsigned int offset; ++ ++ export_length = (pmpz_sizeinbase(value, 2) + 7) / 8; ++ assert(export_length <= length); ++ offset = length - export_length; ++ memset(output, 0, offset); ++ pmpz_export((BYTE *)output + offset, &export_length, 1, 1, 0, 0, value); ++ if (!export_length) ++ { ++ ERR("Zero export length, value bits %u.\n", (unsigned)pmpz_sizeinbase(value, 2)); ++ memset((BYTE *)output + offset, 0, length - offset); ++ } ++ else ++ { ++ assert(export_length + offset == length); ++ } ++} ++ + static NTSTATUS CDECL key_dh_generate( struct key *key ) + { +- gnutls_datum_t prime, generator, privkey, pubkey; + NTSTATUS status = STATUS_SUCCESS; +- gnutls_dh_params_t dh_params; ++ mpz_t p, psub1, g, privkey, pubkey; + ULONG key_length; ++ unsigned int i; + int ret; + + if (!dh_supported) +@@ -1043,22 +1151,18 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + return STATUS_NOT_IMPLEMENTED; + } + +- if ((ret = pgnutls_dh_params_init( &dh_params ))) +- { +- pgnutls_perror( ret ); +- return STATUS_INTERNAL_ERROR; +- } +- + key_length = key->u.a.bitlen / 8; + + if (!(key->u.a.flags & KEY_FLAG_DH_PARAMS_SET)) + { +- /* Generate parameters, export and then import them back below. +- * The bitlen in dh parameters (which is later used for keys generation) +- * is not set to gnutls_dh_params_generate2 'bits' parameter as one +- * could expect. gnutls_dh_params_generate2 generates 'q' (which is not +- * actually needed for DH) with the estimated bit length and then +- * sets the bit length to the 'q' bitlength. */ ++ gnutls_datum_t prime, generator; ++ gnutls_dh_params_t dh_params; ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } + dh_key_alloc( key ); + + if ((ret = pgnutls_dh_params_generate2( dh_params, key->u.a.bitlen ))) +@@ -1073,6 +1177,8 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + pgnutls_dh_params_deinit( dh_params ); + return STATUS_INTERNAL_ERROR; + } ++ pgnutls_dh_params_deinit( dh_params ); ++ + + export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1), key_length, &prime, 1 ); + export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, +@@ -1083,34 +1189,73 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; + } + +- prime.size = generator.size = key_length; +- prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1); +- generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length; ++ pmpz_init(p); ++ pmpz_init(psub1); ++ pmpz_init(g); ++ pmpz_init(pubkey); ++ pmpz_init(privkey); + +- if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, key->u.a.bitlen ))) ++ import_mpz(p, (BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, key_length); ++ if (!mpz_sgn(p)) + { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; ++ ERR("Got zero modulus.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; + } ++ pmpz_sub_ui(psub1, p, 1); + +- if ((ret = pgnutls_dh_generate_key( dh_params, &privkey, &pubkey ))) ++ import_mpz(g, (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, key_length); ++ if (!mpz_sgn(g)) + { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; ++ ERR("Got zero generator.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; + } ++ for (i = 0; i < 3; ++i) ++ { ++ if ((status = gen_random(key_data(key)->d.privkey, key_length))) ++ { ++ goto done; ++ } ++ import_mpz(privkey, key_data(key)->d.privkey, key_length); + +- export_gnutls_datum( (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, +- key_length, &pubkey, 1 ); +- export_gnutls_datum( key_data(key)->d.privkey, key_length, &privkey, 1); ++ pmpz_mod(privkey, privkey, p); ++ pmpz_powm(pubkey, g, privkey, p); ++ if (p_mpz_cmp_ui(pubkey, 1)) ++ break; ++ } ++ if (i == 3) ++ { ++ ERR("Could not generate key after 3 iterations.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } ++ ++ if (pmpz_cmp(pubkey, psub1) >= 0) ++ { ++ ERR("pubkey > p - 1.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } + +- free( privkey.data ); +- free( pubkey.data ); +- pgnutls_dh_params_deinit( dh_params ); ++ export_mpz(key_data(key)->d.privkey, key_length, privkey); ++ export_mpz((UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, key_length, pubkey); + ++done: ++ pmpz_clear(psub1); ++ pmpz_clear(p); ++ pmpz_clear(g); ++ pmpz_clear(pubkey); ++ pmpz_clear(privkey); + return status; + } ++#else ++static NTSTATUS CDECL key_dh_generate( struct key *key ) ++{ ++ ERR("Compiled without DH support.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++} ++#endif + + static NTSTATUS key_asymmetric_generate( void *args ) + { +@@ -2518,8 +2663,6 @@ static NTSTATUS key_secret_agreement( void *args ) + struct secret *secret; + struct key *priv_key; + struct key *peer_key; +- int ret; +- + priv_key = params->privkey; + peer_key = params->pubkey; + secret = params->secret; +@@ -2527,9 +2670,9 @@ static NTSTATUS key_secret_agreement( void *args ) + switch (priv_key->alg_id) + { + case ALG_ID_DH: ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) + { +- gnutls_datum_t prime, generator, priv, peer, secret_datum; +- gnutls_dh_params_t dh_params; ++ mpz_t p, priv, peer, k; + ULONG key_length; + + if (!dh_supported) +@@ -2538,51 +2681,46 @@ static NTSTATUS key_secret_agreement( void *args ) + return STATUS_NOT_IMPLEMENTED; + } + +- if ((ret = pgnutls_dh_params_init( &dh_params ))) +- { +- pgnutls_perror( ret ); +- return STATUS_INTERNAL_ERROR; +- } +- + key_length = priv_key->u.a.bitlen / 8; + +- prime.size = generator.size = key_length; +- prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1); +- generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1) + key_length; +- +- if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, priv_key->u.a.bitlen ))) +- { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; +- } +- +- priv.size = peer.size = key_length; +- priv.data = key_data(priv_key)->d.privkey; +- peer.data = key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2; +- + if (memcmp((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, + key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB), key_length * 2)) + { + ERR("peer DH paramaters do not match.\n"); +- pgnutls_dh_params_deinit( dh_params ); + return STATUS_INTERNAL_ERROR; + } + +- if ((ret = pgnutls_dh_compute_key( dh_params, &priv, NULL, &peer, &secret_datum ))) ++ pmpz_init(p); ++ pmpz_init(priv); ++ pmpz_init(peer); ++ pmpz_init(k); ++ ++ import_mpz(p, (BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, key_length); ++ if (pmpz_sizeinbase(p, 2) < 2) + { +- ERR("Error computing shared key.\n"); +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); ++ ERR("Invalid prime.\n"); ++ pmpz_clear(p); ++ pmpz_clear(priv); ++ pmpz_clear(peer); ++ pmpz_clear(k); + return STATUS_INTERNAL_ERROR; + } +- +- TRACE("secret_datum.size %u, key_length %u.\n", secret_datum.size, (int)key_length); +- export_gnutls_datum( secret->data, key_length, &secret_datum, 1 ); ++ import_mpz(priv, key_data(priv_key)->d.privkey, key_length); ++ import_mpz(peer, key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2, key_length); ++ pmpz_powm(k, peer, priv, p); ++ export_mpz(secret->data, key_length, k); + secret->data_len = key_length; +- free( secret_datum.data ); ++ ++ pmpz_clear(p); ++ pmpz_clear(priv); ++ pmpz_clear(peer); ++ pmpz_clear(k); + break; + } ++#else ++ ERR_(winediag)("Compiled without DH support.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++#endif + + case ALG_ID_ECDH_P256: + FIXME("ECDH is not supported.\n"); diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes7.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes7.mypatch new file mode 100644 index 000000000..803cb0724 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes7.mypatch @@ -0,0 +1,1626 @@ +From 29fb0539c5c1fb065311a73a12fe5500c84c3bc4 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 7 Dec 2020 12:59:55 +0300 +Subject: [PATCH] bcrypt: Implement DH. + +--- + dlls/bcrypt/bcrypt_internal.h | 18 ++ + dlls/bcrypt/bcrypt_main.c | 126 +++++++++++- + dlls/bcrypt/gnutls.c | 359 +++++++++++++++++++++++++++++++++- + include/bcrypt.h | 29 +++ + 4 files changed, 522 insertions(+), 10 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h +index 98223e90ac6..f0eaa6ad4fb 100644 +--- a/dlls/bcrypt/bcrypt_internal.h ++++ b/dlls/bcrypt/bcrypt_internal.h +@@ -131,6 +131,7 @@ enum alg_id + ALG_ID_RSA, + + /* secret agreement */ ++ ALG_ID_DH, + ALG_ID_ECDH_P256, + ALG_ID_ECDH_P384, + +@@ -173,6 +174,8 @@ struct key_symmetric + }; + + #define KEY_FLAG_LEGACY_DSA_V2 0x00000001 ++#define KEY_FLAG_DH_PARAMS_SET 0x00000002 ++#define KEY_FLAG_FINALIZED 0x00000004 + + struct key_asymmetric + { +@@ -196,6 +199,8 @@ struct key + struct secret + { + struct object hdr; ++ UCHAR *data; ++ ULONG data_len; + }; + + struct key_symmetric_set_auth_data_params +@@ -281,6 +286,9 @@ struct key_asymmetric_verify_params + + #define KEY_EXPORT_FLAG_PUBLIC 0x00000001 + #define KEY_EXPORT_FLAG_RSA_FULL 0x00000002 ++#define KEY_EXPORT_FLAG_DH_FULL 0x00000004 ++#define KEY_EXPORT_FLAG_DH_PARAMETERS 0x00000008 ++ + struct key_asymmetric_export_params + { + struct key *key; +@@ -291,6 +299,8 @@ struct key_asymmetric_export_params + }; + + #define KEY_IMPORT_FLAG_PUBLIC 0x00000001 ++#define KEY_IMPORT_FLAG_DH_FULL 0x00000004 ++#define KEY_IMPORT_FLAG_DH_PARAMETERS 0x00000008 + struct key_asymmetric_import_params + { + struct key *key; +@@ -299,6 +309,13 @@ struct key_asymmetric_import_params + ULONG len; + }; + ++struct key_secret_agreement_params ++{ ++ struct key *privkey; ++ struct key *pubkey; ++ struct secret *secret; ++}; ++ + enum key_funcs + { + unix_process_attach, +@@ -318,7 +335,8 @@ enum key_funcs + unix_key_asymmetric_destroy, + unix_key_asymmetric_export, + unix_key_asymmetric_import, ++ unix_key_secret_agreement, + unix_funcs_count, + }; + + #endif /* __BCRYPT_INTERNAL_H */ +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index cf05e8b4d44..72a7b1915f2 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -112,6 +112,7 @@ builtin_algorithms[] = + { BCRYPT_MD4_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 512 }, + { BCRYPT_MD2_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 128 }, + { BCRYPT_RSA_ALGORITHM, BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE, 0, 0, 0 }, ++ { BCRYPT_DH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P256_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P384_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_RSA_SIGN_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, +@@ -870,6 +871,20 @@ static NTSTATUS get_hash_property( const struct hash *hash, const WCHAR *prop, U + return status; + } + ++static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) ++{ ++ struct key_asymmetric_export_params params; ++ ++ if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED; ++ ++ params.key = (struct key *)key; ++ params.flags = KEY_EXPORT_FLAG_DH_PARAMETERS; ++ params.buf = buf; ++ params.len = size; ++ params.ret_len = ret_size; ++ return UNIX_CALL( key_asymmetric_export, ¶ms ); ++} ++ + static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) + { + switch (key->alg_id) +@@ -881,6 +896,9 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA + if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED; + return get_aes_property( key->u.s.mode, prop, buf, size, ret_size ); + ++ case ALG_ID_DH: ++ return get_dh_property( key, prop, buf, size, ret_size ); ++ + default: + FIXME( "unsupported algorithm %u\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -1130,6 +1148,8 @@ static NTSTATUS key_asymmetric_create( enum alg_id alg_id, ULONG bitlen, struct + return STATUS_NOT_IMPLEMENTED; + } + ++ if (alg_id == ALG_ID_DH && bitlen < 512) return STATUS_INVALID_PARAMETER; ++ + if (!(key = calloc( 1, sizeof(*key) ))) return STATUS_NO_MEMORY; + key->hdr.magic = MAGIC_KEY; + key->alg_id = alg_id; +@@ -1269,6 +1289,7 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY + static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size ) + { + struct key_asymmetric_export_params params; ++ BOOL dh_private = FALSE; + + if (!wcscmp( type, BCRYPT_KEY_DATA_BLOB )) + { +@@ -1328,6 +1349,15 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U + params.ret_len = size; + return UNIX_CALL( key_asymmetric_export, ¶ms ); + } ++ else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ) || (dh_private = !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))) ++ { ++ params.key = key; ++ params.flags = dh_private ? KEY_EXPORT_FLAG_DH_FULL : 0; ++ params.buf = output; ++ params.len = output_len; ++ params.ret_len = size; ++ return UNIX_CALL( key_asymmetric_export, ¶ms ); ++ } + + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_IMPLEMENTED; +@@ -1529,7 +1559,6 @@ static void key_destroy( struct key *key ) + } + else + UNIX_CALL( key_asymmetric_destroy, key ); +- + destroy_object( &key->hdr ); + } + +@@ -1537,6 +1566,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + ULONG input_len ) + { + struct key_asymmetric_import_params params; ++ BOOL dh_private = FALSE; + struct key *key; + NTSTATUS status; + ULONG size; +@@ -1591,6 +1621,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) +@@ -1638,6 +1669,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB )) +@@ -1663,6 +1695,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) +@@ -1685,6 +1718,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB )) +@@ -1707,6 +1741,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB )) +@@ -1747,6 +1782,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB )) /* not supported on native */ +@@ -1783,6 +1819,41 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP + } + + *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; ++ return STATUS_SUCCESS; ++ } ++ else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB ) || (dh_private = !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB ))) ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)input; ++ ULONG size; ++ ++ if (alg->id != ALG_ID_DH) return STATUS_NOT_SUPPORTED; ++ if (h->dwMagic != (dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC)) ++ { ++ WARN("unexpected dwMagic %#lx.\n", h->dwMagic); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ size = sizeof(*h) + h->cbKey * 3; ++ if (dh_private) ++ size += h->cbKey; ++ if (input_len != size) return STATUS_INVALID_PARAMETER; ++ if (h->cbKey * 8 < 512) return STATUS_INVALID_PARAMETER; ++ ++ if ((status = key_asymmetric_create( alg->id, h->cbKey * 8, &key ))) return status; ++ ++ params.key = key; ++ params.flags = dh_private ? KEY_IMPORT_FLAG_DH_FULL : 0; ++ params.buf = input; ++ params.len = input_len; ++ if ((status = UNIX_CALL( key_asymmetric_import, ¶ms ))) ++ { ++ key_destroy( key ); ++ return status; ++ } ++ ++ *ret_key = key; ++ key->u.a.flags |= KEY_FLAG_FINALIZED; + return STATUS_SUCCESS; + } + +@@ -1828,11 +1899,15 @@ NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HAND + NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags ) + { + struct key *key = get_key_object( handle ); ++ NTSTATUS ret; + + TRACE( "%p, %#lx\n", key, flags ); + + if (!key) return STATUS_INVALID_HANDLE; +- return UNIX_CALL( key_asymmetric_generate, key ); ++ if (!(ret = UNIX_CALL( key_asymmetric_generate, key ))) ++ key->u.a.flags |= KEY_FLAG_FINALIZED; ++ ++ return ret; + } + + NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key, const WCHAR *type, +@@ -2146,6 +2221,21 @@ NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHA + case MAGIC_KEY: + { + struct key *key = (struct key *)object; ++ ++ if (key->alg_id == ALG_ID_DH) ++ { ++ if (!lstrcmpW( prop, BCRYPT_DH_PARAMETERS )) ++ { ++ struct key_asymmetric_import_params params; ++ ++ params.key = key; ++ params.flags = KEY_IMPORT_FLAG_DH_PARAMETERS; ++ params.buf = value; ++ params.len = size; ++ return UNIX_CALL( key_asymmetric_import, ¶ms ); ++ } ++ return STATUS_NOT_IMPLEMENTED; ++ } + return set_key_property( key, prop, value, size, flags ); + } + default: +@@ -2308,30 +2398,54 @@ NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULO + NTSTATUS WINAPI BCryptSecretAgreement( BCRYPT_KEY_HANDLE privkey_handle, BCRYPT_KEY_HANDLE pubkey_handle, + BCRYPT_SECRET_HANDLE *ret_handle, ULONG flags ) + { ++ struct key_secret_agreement_params params; + struct key *privkey = get_key_object( privkey_handle ); + struct key *pubkey = get_key_object( pubkey_handle ); + struct secret *secret; ++ NTSTATUS status; + + FIXME( "%p, %p, %p, %#lx\n", privkey_handle, pubkey_handle, ret_handle, flags ); + + if (!privkey || !pubkey) return STATUS_INVALID_HANDLE; + if (!is_agreement_key( privkey ) || !is_agreement_key( pubkey )) return STATUS_NOT_SUPPORTED; + if (!ret_handle) return STATUS_INVALID_PARAMETER; ++ if (is_symmetric_key( privkey ) || privkey->alg_id != pubkey->alg_id) return STATUS_INVALID_PARAMETER; ++ if (!(privkey->u.a.flags & pubkey->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_PARAMETER; ++ if (privkey->u.a.bitlen != pubkey->u.a.bitlen) return STATUS_INVALID_PARAMETER; + + if (!(secret = calloc( 1, sizeof(*secret) ))) return STATUS_NO_MEMORY; +- secret->hdr.magic = MAGIC_SECRET; ++ if (!(secret->data = malloc( privkey->u.a.bitlen / 8 ))) ++ { ++ free( secret ); ++ return STATUS_NO_MEMORY; ++ } + +- *ret_handle = secret; +- return STATUS_SUCCESS; ++ params.privkey = privkey; ++ params.pubkey = pubkey; ++ params.secret = secret; ++ ++ if ((status = UNIX_CALL( key_secret_agreement, ¶ms ))) ++ { ++ free( secret->data ); ++ free( secret ); ++ } ++ else ++ { ++ secret->hdr.magic = MAGIC_SECRET; ++ *ret_handle = secret; ++ } ++ return status; + } + + NTSTATUS WINAPI BCryptDestroySecret( BCRYPT_SECRET_HANDLE handle ) + { + struct secret *secret = get_secret_object( handle ); + +- FIXME( "%p\n", handle ); ++ TRACE( "%p\n", handle ); + + if (!secret) return STATUS_INVALID_HANDLE; ++ secret->hdr.magic = 0; ++ free( secret->data ); + destroy_object( &secret->hdr ); + return STATUS_SUCCESS; + } +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 89e5c91776d..4a9f223a838 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -79,6 +79,12 @@ typedef enum + typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t; + #endif + ++struct dh_key_data ++{ ++ UCHAR *pubkey; /* Used for DH private key only. */ ++ UCHAR *privkey; /* Used for DH private key only. */ ++}; ++ + union key_data + { + gnutls_cipher_hd_t cipher; +@@ -87,6 +93,11 @@ union key_data + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + } a; ++ struct /* DH */ ++ { ++ UCHAR *privkey; ++ UCHAR *pubkey; ++ } d; + }; + C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) ); + +@@ -95,6 +106,29 @@ static union key_data *key_data( struct key *key ) + return (union key_data *)key->private; + } + ++static unsigned int dh_pubkey_len( struct key *key ) ++{ ++ return sizeof(BCRYPT_DH_KEY_BLOB) + key->u.a.bitlen / 8 * 3; ++} ++ ++static void dh_key_free( struct key *key ) ++{ ++ free( key_data(key)->d.privkey ); ++ free( key_data(key)->d.pubkey ); ++} ++ ++static void dh_key_alloc( struct key *key ) ++{ ++ unsigned int bitlen = key->u.a.bitlen; ++ ++ if (key_data(key)->d.pubkey) return; ++ ++ key_data(key)->d.privkey = calloc( 1, bitlen / 8 ); ++ key_data(key)->d.pubkey = calloc( 1, dh_pubkey_len( key )); ++} ++ ++static BOOL dh_supported; ++ + /* Not present in gnutls version < 3.0 */ + static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t); + static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); +@@ -144,6 +178,17 @@ static void (*pgnutls_x509_spki_set_rsa_pss_params)(gnutls_x509_spki_t, gnutls_d + static int (*pgnutls_pubkey_set_spki)(gnutls_pubkey_t, const gnutls_x509_spki_t, unsigned int); + static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_t, unsigned int); + ++static int (*pgnutls_dh_params_init)(gnutls_dh_params_t * dh_params); ++static void (*pgnutls_dh_params_deinit)(gnutls_dh_params_t dh_params); ++static int (*pgnutls_dh_params_generate2)(gnutls_dh_params_t dparams, unsigned int bits); ++static int (*pgnutls_dh_params_import_raw2)(gnutls_dh_params_t dh_params, const gnutls_datum_t * prime, ++ const gnutls_datum_t * generator, unsigned key_bits); ++static int (*pgnutls_dh_params_export_raw)(gnutls_dh_params_t params, gnutls_datum_t * prime, ++ gnutls_datum_t * generator, unsigned int *bits); ++static int (*pgnutls_dh_generate_key)(gnutls_dh_params_t dh_params, gnutls_datum_t *priv_key, gnutls_datum_t *pub_key); ++static int (*pgnutls_dh_compute_key)(gnutls_dh_params_t dh_params, const gnutls_datum_t *priv_key, ++ const gnutls_datum_t *pub_key, const gnutls_datum_t *peer_key, gnutls_datum_t *Z); ++ + static void *libgnutls_handle; + #define MAKE_FUNCPTR(f) static typeof(f) * p##f + MAKE_FUNCPTR(gnutls_cipher_decrypt2); +@@ -392,6 +437,39 @@ static NTSTATUS gnutls_process_attach( void *args ) + pgnutls_perror( ret ); + goto fail; + } ++ if (!(pgnutls_dh_params_init = dlsym( libgnutls_handle, "gnutls_dh_params_init" ))) ++ { ++ WARN("gnutls_dh_params_init not found\n"); ++ } ++ if (!(pgnutls_dh_params_deinit = dlsym( libgnutls_handle, "gnutls_dh_params_deinit" ))) ++ { ++ WARN("gnutls_dh_params_deinit not found\n"); ++ } ++ if (!(pgnutls_dh_params_generate2 = dlsym( libgnutls_handle, "gnutls_dh_params_generate2" ))) ++ { ++ WARN("gnutls_dh_params_generate2 not found\n"); ++ } ++ if (!(pgnutls_dh_params_import_raw2 = dlsym( libgnutls_handle, "gnutls_dh_params_import_raw2" ))) ++ { ++ WARN("gnutls_dh_params_import_raw2 not found\n"); ++ } ++ if (!(pgnutls_dh_params_export_raw = dlsym( libgnutls_handle, "gnutls_dh_params_export_raw" ))) ++ { ++ WARN("gnutls_dh_params_export_raw not found\n"); ++ } ++ if (!(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "_gnutls_dh_generate_key" )) ++ && !(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "gnutls_dh_generate_key" ))) ++ { ++ WARN("gnutls_dh_generate_key not found\n"); ++ } ++ if (!(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "_gnutls_dh_compute_key" )) ++ && !(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "gnutls_dh_compute_key" ))) ++ { ++ WARN("gnutls_dh_compute_key not found\n"); ++ } ++ ++ dh_supported = pgnutls_dh_params_init && pgnutls_dh_params_generate2 && pgnutls_dh_params_import_raw2 ++ && pgnutls_dh_generate_key && pgnutls_dh_compute_key; + + if (TRACE_ON( bcrypt )) + { +@@ -951,6 +1029,89 @@ static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l + return status; + } + ++static NTSTATUS CDECL key_dh_generate( struct key *key ) ++{ ++ gnutls_datum_t prime, generator, privkey, pubkey; ++ NTSTATUS status = STATUS_SUCCESS; ++ gnutls_dh_params_t dh_params; ++ ULONG key_length; ++ int ret; ++ ++ if (!dh_supported) ++ { ++ ERR("DH is not available.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ key_length = key->u.a.bitlen / 8; ++ ++ if (!(key->u.a.flags & KEY_FLAG_DH_PARAMS_SET)) ++ { ++ /* Generate parameters, export and then import them back below. ++ * The bitlen in dh parameters (which is later used for keys generation) ++ * is not set to gnutls_dh_params_generate2 'bits' parameter as one ++ * could expect. gnutls_dh_params_generate2 generates 'q' (which is not ++ * actually needed for DH) with the estimated bit length and then ++ * sets the bit length to the 'q' bitlength. */ ++ dh_key_alloc( key ); ++ ++ if ((ret = pgnutls_dh_params_generate2( dh_params, key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ if ((ret = pgnutls_dh_params_export_raw( dh_params, &prime, &generator, NULL ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1), key_length, &prime, 1 ); ++ export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, ++ key_length, &generator, 1 ); ++ free( prime.data ); ++ free( generator.data ); ++ ++ key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; ++ } ++ ++ prime.size = generator.size = key_length; ++ prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1); ++ generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length; ++ ++ if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if ((ret = pgnutls_dh_generate_key( dh_params, &privkey, &pubkey ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ export_gnutls_datum( (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, ++ key_length, &pubkey, 1 ); ++ export_gnutls_datum( key_data(key)->d.privkey, key_length, &privkey, 1); ++ ++ free( privkey.data ); ++ free( pubkey.data ); ++ pgnutls_dh_params_deinit( dh_params ); ++ ++ return status; ++} ++ + static NTSTATUS key_asymmetric_generate( void *args ) + { + struct key *key = args; +@@ -961,7 +1122,7 @@ static NTSTATUS key_asymmetric_generate( void *args ) + int ret; + + if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; +- if (key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; ++ if (key->alg_id != ALG_ID_DH && key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; + + switch (key->alg_id) + { +@@ -988,6 +1149,9 @@ static NTSTATUS key_asymmetric_generate( void *args ) + bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 ); + break; + ++ case ALG_ID_DH: ++ return key_dh_generate( key ); ++ + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return STATUS_NOT_SUPPORTED; +@@ -1000,6 +1164,7 @@ static NTSTATUS key_asymmetric_generate( void *args ) + } + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { ++ ERR("gnutls error bitlen %u.\n", bitlen); + pgnutls_perror( ret ); + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; +@@ -1557,6 +1722,44 @@ static NTSTATUS key_asymmetric_export( void *args ) + return key_export_dsa_capi( key, params->buf, params->len, params->ret_len ); + return STATUS_NOT_IMPLEMENTED; + ++ case ALG_ID_DH: ++ if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; ++ if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS) ++ { ++ BCRYPT_DH_PARAMETER_HEADER *h; ++ unsigned int data_size; ++ ++ data_size = sizeof(BCRYPT_DH_PARAMETER_HEADER) + key->u.a.bitlen / 8 * 2; ++ if (params->ret_len) *params->ret_len = data_size; ++ if (!params->buf) return STATUS_SUCCESS; ++ if (params->len < data_size) return STATUS_BUFFER_TOO_SMALL; ++ ++ h = (BCRYPT_DH_PARAMETER_HEADER *)params->buf; ++ h->cbLength = data_size; ++ h->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; ++ h->cbKeyLength = key->u.a.bitlen / 8; ++ memcpy( h + 1, (BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, h->cbKeyLength * 2); ++ } ++ else ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)params->buf; ++ BOOL dh_private = flags & KEY_EXPORT_FLAG_DH_FULL; ++ ++ *params->ret_len = dh_pubkey_len( key ); ++ if (dh_private) ++ *params->ret_len += key->u.a.bitlen / 8; ++ ++ if (params->len < *params->ret_len) return STATUS_SUCCESS; ++ ++ memcpy(params->buf, key_data(key)->d.pubkey, dh_pubkey_len( key )); ++ if (dh_private) ++ memcpy(params->buf + dh_pubkey_len( key ), key_data(key)->d.privkey, key->u.a.bitlen / 8); ++ ++ h->dwMagic = dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC; ++ h->cbKey = key->u.a.bitlen / 8; ++ } ++ return STATUS_SUCCESS; ++ + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -1604,6 +1807,49 @@ static NTSTATUS key_asymmetric_import( void *args ) + FIXME( "DSA private key not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + ++ case ALG_ID_DH: ++ if (flags & KEY_IMPORT_FLAG_DH_PARAMETERS) ++ { ++ const BCRYPT_DH_PARAMETER_HEADER *h = (const BCRYPT_DH_PARAMETER_HEADER *)params->buf; ++ ULONG param_size = sizeof(BCRYPT_DH_PARAMETER_HEADER) + key->u.a.bitlen / 8 * 2; ++ ++ if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; ++ if (params->len < param_size) return STATUS_BUFFER_TOO_SMALL; ++ if (!h || h->cbLength != param_size || h->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC ++ || h->cbKeyLength != key->u.a.bitlen / 8) ++ return STATUS_INVALID_PARAMETER; ++ ++ dh_key_alloc( key ); ++ memcpy((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, h + 1, h->cbKeyLength * 2); ++ key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; ++ } ++ else ++ { ++ BCRYPT_DH_KEY_BLOB *h = (BCRYPT_DH_KEY_BLOB *)params->buf; ++ BOOL dh_private = flags & KEY_IMPORT_FLAG_DH_FULL; ++ ULONG size; ++ ++ if (h->dwMagic != (dh_private ? BCRYPT_DH_PRIVATE_MAGIC : BCRYPT_DH_PUBLIC_MAGIC)) ++ { ++ WARN("unexpected dwMagic %#x.\n", (int)h->dwMagic); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ size = sizeof(*h) + h->cbKey * 3; ++ if (dh_private) ++ size += h->cbKey; ++ if (params->len != size) return STATUS_INVALID_PARAMETER; ++ if (h->cbKey * 8 < 512) return STATUS_INVALID_PARAMETER; ++ ++ dh_key_alloc( key ); ++ ++ memcpy( key_data(key)->d.pubkey, params->buf, dh_pubkey_len( key )); ++ ++ if (dh_private) ++ memcpy( key_data(key)->d.privkey, params->buf + sizeof(*h) + h->cbKey * 3, h->cbKey); ++ } ++ return STATUS_SUCCESS; ++ + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; +@@ -2018,8 +2264,15 @@ static NTSTATUS key_asymmetric_destroy( void *args ) + { + struct key *key = args; + +- if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); +- if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); ++ if (key->alg_id == ALG_ID_DH) ++ { ++ dh_key_free( key ); ++ } ++ else ++ { ++ if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); ++ if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); ++ } + return STATUS_SUCCESS; + } + +@@ -2094,6 +2347,7 @@ static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy ) + } + break; + } ++ + default: + ERR( "unhandled algorithm %u\n", key_orig->alg_id ); + return STATUS_INTERNAL_ERROR; +@@ -2187,6 +2441,19 @@ static NTSTATUS key_asymmetric_duplicate( void *args ) + const struct key_asymmetric_duplicate_params *params = args; + NTSTATUS status; + ++ if (params->key_orig->alg_id == ALG_ID_DH) ++ { ++ union key_data *s = key_data( params->key_orig ); ++ union key_data *d = key_data( params->key_copy ); ++ if (s->d.privkey) ++ { ++ dh_key_alloc( params->key_copy ); ++ memcpy( d->d.privkey, s->d.privkey, params->key_orig->u.a.bitlen / 8 ); ++ memcpy( d->d.pubkey, s->d.pubkey, dh_pubkey_len( params->key_orig )); ++ } ++ return STATUS_SUCCESS; ++ } ++ + if (key_data(params->key_orig)->a.privkey && (status = dup_privkey( params->key_orig, params->key_copy ))) + return status; + +@@ -2245,6 +2512,89 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) + return status; + } + ++static NTSTATUS key_secret_agreement( void *args ) ++{ ++ struct key_secret_agreement_params *params = args; ++ struct secret *secret; ++ struct key *priv_key; ++ struct key *peer_key; ++ int ret; ++ ++ priv_key = params->privkey; ++ peer_key = params->pubkey; ++ secret = params->secret; ++ ++ switch (priv_key->alg_id) ++ { ++ case ALG_ID_DH: ++ { ++ gnutls_datum_t prime, generator, priv, peer, secret_datum; ++ gnutls_dh_params_t dh_params; ++ ULONG key_length; ++ ++ if (!dh_supported) ++ { ++ ERR("DH is not available.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ key_length = priv_key->u.a.bitlen / 8; ++ ++ prime.size = generator.size = key_length; ++ prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1); ++ generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1) + key_length; ++ ++ if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, priv_key->u.a.bitlen ))) ++ { ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ priv.size = peer.size = key_length; ++ priv.data = key_data(priv_key)->d.privkey; ++ peer.data = key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2; ++ ++ if (memcmp((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, ++ key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB), key_length * 2)) ++ { ++ ERR("peer DH paramaters do not match.\n"); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if ((ret = pgnutls_dh_compute_key( dh_params, &priv, NULL, &peer, &secret_datum ))) ++ { ++ ERR("Error computing shared key.\n"); ++ pgnutls_perror( ret ); ++ pgnutls_dh_params_deinit( dh_params ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ TRACE("secret_datum.size %u, key_length %u.\n", secret_datum.size, (int)key_length); ++ export_gnutls_datum( secret->data, key_length, &secret_datum, 1 ); ++ secret->data_len = key_length; ++ free( secret_datum.data ); ++ break; ++ } ++ ++ case ALG_ID_ECDH_P256: ++ FIXME("ECDH is not supported.\n"); ++ break; ++ ++ default: ++ ERR( "unhandled algorithm %u\n", priv_key->alg_id ); ++ return STATUS_INVALID_HANDLE; ++ } ++ return STATUS_SUCCESS; ++} ++ + const unixlib_entry_t __wine_unix_call_funcs[] = + { + gnutls_process_attach, +@@ -2263,7 +2613,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + key_asymmetric_verify, + key_asymmetric_destroy, + key_asymmetric_export, +- key_asymmetric_import ++ key_asymmetric_import, ++ key_secret_agreement + }; + + #ifdef _WIN64 +diff --git a/include/bcrypt.h b/include/bcrypt.h +index 6822491ed36..34c79d9cbf4 100644 +--- a/include/bcrypt.h ++++ b/include/bcrypt.h +@@ -62,6 +62,8 @@ typedef LONG NTSTATUS; + #define BCRYPT_OPAQUE_KEY_BLOB L"OpaqueKeyBlob" + #define BCRYPT_KEY_DATA_BLOB L"KeyDataBlob" + #define BCRYPT_AES_WRAP_KEY_BLOB L"Rfc3565KeyWrapBlob" ++#define BCRYPT_DH_PUBLIC_BLOB L"DHPUBLICBLOB" ++#define BCRYPT_DH_PRIVATE_BLOB L"DHPRIVATEBLOB" + #define BCRYPT_ECCPUBLIC_BLOB L"ECCPUBLICBLOB" + #define BCRYPT_ECCPRIVATE_BLOB L"ECCPRIVATEBLOB" + #define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB" +@@ -82,6 +84,7 @@ typedef LONG NTSTATUS; + #define BCRYPT_3DES_ALGORITHM L"3DES" + #define BCRYPT_AES_ALGORITHM L"AES" + #define BCRYPT_DES_ALGORITHM L"DES" ++#define BCRYPT_DH_ALGORITHM L"DH" + #define BCRYPT_DSA_ALGORITHM L"DSA" + #define BCRYPT_ECDH_P256_ALGORITHM L"ECDH_P256" + #define BCRYPT_ECDH_P384_ALGORITHM L"ECDH_P384" +@@ -113,6 +116,8 @@ typedef LONG NTSTATUS; + #define BCRYPT_KDF_TLS_PRF L"TLS_PRF" + #define BCRYPT_KDF_SP80056A_CONCAT L"SP800_56A_CONCAT" + #define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" ++ ++#define BCRYPT_DH_PARAMETERS L"DHParameters" + #else + static const WCHAR BCRYPT_ALGORITHM_NAME[] = {'A','l','g','o','r','i','t','h','m','N','a','m','e',0}; + static const WCHAR BCRYPT_AUTH_TAG_LENGTH[] = {'A','u','t','h','T','a','g','L','e','n','g','t','h',0}; +@@ -135,6 +140,8 @@ static const WCHAR BCRYPT_SIGNATURE_LENGTH[] = {'S','i','g','n','a','t','u','r', + static const WCHAR BCRYPT_OPAQUE_KEY_BLOB[] = {'O','p','a','q','u','e','K','e','y','B','l','o','b',0}; + static const WCHAR BCRYPT_KEY_DATA_BLOB[] = {'K','e','y','D','a','t','a','B','l','o','b',0}; + static const WCHAR BCRYPT_AES_WRAP_KEY_BLOB[] = {'R','f','c','3','5','6','5','K','e','y','W','r','a','p','B','l','o','b',0}; ++static const WCHAR BCRYPT_DH_PUBLIC_BLOB[] = {'D','H','P','U','B','L','I','C','B','L','O','B',0}; ++static const WCHAR BCRYPT_DH_PRIVATE_BLOB[] = {'D','H','P','R','I','V','A','T','E','B','L','O','B',0}; + static const WCHAR BCRYPT_ECCPUBLIC_BLOB[] = {'E','C','C','P','U','B','L','I','C','B','L','O','B',0}; + static const WCHAR BCRYPT_ECCPRIVATE_BLOB[] = {'E','C','C','P','R','I','V','A','T','E','B','L','O','B',0}; + static const WCHAR BCRYPT_RSAPUBLIC_BLOB[] = {'R','S','A','P','U','B','L','I','C','B','L','O','B',0}; +@@ -157,6 +164,7 @@ static const WCHAR MS_PLATFORM_CRYPTO_PROVIDER[] = \ + static const WCHAR BCRYPT_3DES_ALGORITHM[] = {'3','D','E','S',0}; + static const WCHAR BCRYPT_AES_ALGORITHM[] = {'A','E','S',0}; + static const WCHAR BCRYPT_DES_ALGORITHM[] = {'D','E','S',0}; ++static const WCHAR BCRYPT_DH_ALGORITHM[] = {'D','H',0}; + static const WCHAR BCRYPT_DSA_ALGORITHM[] = {'D','S','A',0}; + static const WCHAR BCRYPT_ECDH_P256_ALGORITHM[] = {'E','C','D','H','_','P','2','5','6',0}; + static const WCHAR BCRYPT_ECDH_P384_ALGORITHM[] = {'E','C','D','H','_','P','3','8','4',0}; +@@ -188,6 +196,7 @@ static const WCHAR BCRYPT_KDF_HMAC[] = {'H','M','A','C',0}; + static const WCHAR BCRYPT_KDF_TLS_PRF[] = {'T','L','S','_','P','R','F',0}; + static const WCHAR BCRYPT_KDF_SP80056A_CONCAT[] = {'S','P','8','0','0','_','5','6','A','_','C','O','N','C','A','T',0}; + static const WCHAR BCRYPT_KDF_RAW_SECRET[] = {'T','R','U','N','C','A','T','E',0}; ++#define BCRYPT_DH_PARAMETERS u"DHParameters" + #endif + + #define BCRYPT_ECDSA_PUBLIC_P256_MAGIC 0x31534345 +@@ -320,6 +329,15 @@ typedef struct _BCRYPT_DSA_KEY_BLOB + #define BCRYPT_DSA_PUBLIC_MAGIC_V2 0x32425044 + #define BCRYPT_DSA_PRIVATE_MAGIC_V2 0x32565044 + ++typedef struct _BCRYPT_DH_KEY_BLOB ++{ ++ ULONG dwMagic; ++ ULONG cbKey; ++} BCRYPT_DH_KEY_BLOB, *PBCRYPT_DH_KEY_BLOB; ++ ++#define BCRYPT_DH_PUBLIC_MAGIC 0x42504844 ++#define BCRYPT_DH_PRIVATE_MAGIC 0x56504844 ++ + typedef enum + { + DSA_HASH_ALGORITHM_SHA1, +@@ -379,6 +397,15 @@ typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER + ULONG cbKeyData; + } BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER; + ++typedef struct _BCRYPT_DH_PARAMETER_HEADER ++{ ++ ULONG cbLength; ++ ULONG dwMagic; ++ ULONG cbKeyLength; ++} BCRYPT_DH_PARAMETER_HEADER; ++ ++#define BCRYPT_DH_PARAMETERS_MAGIC 0x4d504844 ++ + #define KDF_HASH_ALGORITHM 0x00000000 + #define KDF_SECRET_PREPEND 0x00000001 + #define KDF_SECRET_APPEND 0x00000002 +@@ -408,6 +435,8 @@ typedef PVOID BCRYPT_HANDLE; + typedef PVOID BCRYPT_HASH_HANDLE; + typedef PVOID BCRYPT_SECRET_HANDLE; + ++#define BCRYPT_NO_KEY_VALIDATION 0x00000008 ++ + /* Pseudo handles */ + #define BCRYPT_MD2_ALG_HANDLE ((BCRYPT_ALG_HANDLE)0x00000001) + #define BCRYPT_MD4_ALG_HANDLE ((BCRYPT_ALG_HANDLE)0x00000011) + +From f730ca784e1d027b674b65c3de0d5b04cf0930f1 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 9 Dec 2020 20:10:13 +0300 +Subject: [PATCH] bcrypt: Implement BCryptDeriveKey() for _KDF_RAW_SECRET. + +Extracted from the patch implementing ECDH on top of gcrypt by Derek Lesho. +--- + dlls/bcrypt/bcrypt_main.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index 72a7b1915f2..e6016a062c5 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -2455,12 +2455,35 @@ NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, + { + struct secret *secret = get_secret_object( handle ); + +- FIXME( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags ); ++ TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags ); + + if (!secret) return STATUS_INVALID_HANDLE; + if (!kdf) return STATUS_INVALID_PARAMETER; + +- return STATUS_INTERNAL_ERROR; ++ if (flags) FIXME("flags ignored: %#lx\n", flags); ++ ++ if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) ++ { ++ ULONG secret_length = secret->data_len; ++ unsigned int i;; ++ ++ if (!derived) ++ { ++ *result = secret_length; ++ return STATUS_SUCCESS; ++ } ++ ++ /* outputs in little endian for some reason */ ++ for (i = 0; i < min(secret_length, derived_size); i++) ++ { ++ derived[i] = secret->data[secret_length - i - 1]; ++ } ++ ++ *result = i; ++ return STATUS_SUCCESS; ++ } ++ FIXME( "Derivation function %s not supported.\n", debugstr_w(kdf) ); ++ return STATUS_NOT_IMPLEMENTED; + } + + BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) + +From ebc03fd79e25ca53582d14a7098b2cb454a10018 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Tue, 7 Jan 2020 14:22:49 -0600 +Subject: [PATCH] bcrypt: Implement BCRYPT_KDF_HASH. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47699 +Signed-off-by: Derek Lesho +--- + dlls/bcrypt/bcrypt_main.c | 107 ++++++++++++++++++++++++++++++++++++- + dlls/bcrypt/tests/bcrypt.c | 3 +- + 2 files changed, 107 insertions(+), 3 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index e6016a062c5..bcb7a692ffd 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -2462,7 +2462,112 @@ NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, + + if (flags) FIXME("flags ignored: %#lx\n", flags); + +- if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) ++ if (!(lstrcmpW( kdf, BCRYPT_KDF_HASH ))) ++ { ++ unsigned int i; ++ BCryptBuffer *hash_algorithm = NULL; ++ BCryptBuffer *secret_prepend = NULL; ++ BCryptBuffer *secret_append = NULL; ++ enum alg_id hash_alg_id; ++ ULONG hash_length; ++ struct hash_impl hash; ++ NTSTATUS status; ++ ++ if (parameter) ++ { ++ for (i = 0; i < parameter->cBuffers; i++) ++ { ++ BCryptBuffer *cur_buffer = ¶meter->pBuffers[i]; ++ switch(cur_buffer->BufferType) ++ { ++ case KDF_HASH_ALGORITHM: ++ if (hash_algorithm) ++ FIXME("Duplicate KDF_HASH_ALGORITHM, untested\n"); ++ hash_algorithm = cur_buffer; ++ break; ++ case KDF_SECRET_PREPEND: ++ if (secret_prepend) ++ FIXME("Multiple prefixes unsupported\n"); ++ secret_prepend = cur_buffer; ++ break; ++ case KDF_SECRET_APPEND: ++ if (secret_append) ++ FIXME("Multiple suffixes unsupported\n"); ++ secret_append = cur_buffer; ++ break; ++ default: ++ FIXME("Unsupported BCRYPT_KDF_HASH parameter type %#lx\n", cur_buffer->BufferType); ++ break; ++ } ++ } ++ } ++ ++ if (!(hash_algorithm)) ++ hash_alg_id = ALG_ID_SHA1; ++ else ++ { ++ for (i = 0; i < ARRAY_SIZE( builtin_algorithms ); i++) ++ { ++ if (!lstrcmpW( hash_algorithm->pvBuffer, builtin_algorithms[i].name)) ++ { ++ hash_alg_id = i; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(builtin_algorithms)) ++ { ++ WARN("Algorithm %s not found\n", debugstr_w(hash_algorithm->pvBuffer)); ++ return STATUS_NOT_SUPPORTED; ++ } ++ if (builtin_algorithms[hash_alg_id].class != BCRYPT_HASH_INTERFACE) ++ { ++ WARN("Incorrect class %lu\n", builtin_algorithms[hash_alg_id].class); ++ return STATUS_NOT_SUPPORTED; ++ } ++ } ++ ++ hash_length = builtin_algorithms[hash_alg_id].hash_length; ++ ++ if (!derived) ++ { ++ *result = hash_length; ++ return STATUS_SUCCESS; ++ } ++ ++ if ((status = hash_init(&hash, hash_alg_id))) ++ { ++ return status; ++ } ++ ++ if (secret_prepend) ++ { ++ hash_update(&hash, hash_alg_id, secret_prepend->pvBuffer, secret_prepend->cbBuffer); ++ } ++ ++ hash_update(&hash, hash_alg_id, secret->data, secret->data_len); ++ ++ if (secret_append) ++ { ++ hash_update(&hash, hash_alg_id, secret_append->pvBuffer, secret_append->cbBuffer); ++ } ++ ++ if (derived_size >= hash_length) ++ { ++ hash_finish(&hash, hash_alg_id, derived); ++ *result = hash_length; ++ } ++ else ++ { ++ UCHAR *output = malloc(hash_length); ++ hash_finish(&hash, hash_alg_id, output); ++ memcpy(derived, output, derived_size); ++ free(output); ++ *result = derived_size; ++ } ++ ++ return STATUS_SUCCESS; ++ } ++ else if (!(lstrcmpW( kdf, BCRYPT_KDF_RAW_SECRET ))) + { + ULONG secret_length = secret->data_len; + unsigned int i;; + + +From b0c2492c38a33c37e8877a1572eb0679b948a96f Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Dec 2020 04:07:13 +0300 +Subject: [PATCH] bcrypt: Reimplement DH using libgmp instead of private gnutls + functions. + +--- + configure.ac | 8 ++ + dlls/bcrypt/Makefile.in | 2 +- + dlls/bcrypt/gnutls.c | 300 +++++++++++++++++++++++++++++----------- + 3 files changed, 228 insertions(+), 82 deletions(-) + +diff --git a/configure.ac b/configure.ac +index a7268c12d15..c9bc5897f91 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1419,6 +1419,14 @@ fi + WINE_WARNING_WITH(gnutls,[test "x$ac_cv_lib_soname_gnutls" = "x"], + [libgnutls ${notice_platform}development files not found, no schannel support.]) + ++dnl **** Check for libgmp **** ++if test "x$with_gnutls" != "xno" ++then ++ WINE_PACKAGE_FLAGS(GMP,[gmp],[-lgmp],,, ++ [AC_CHECK_HEADERS([gmp.h], ++ [WINE_CHECK_SONAME(gmp,__gmpz_init,,[GMP_CFLAGS=""],[$GMP_LIBS],[[libgmp-*]])])]) ++fi ++ + dnl **** Check for SANE **** + if test "x$with_sane" != "xno" + then +diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in +index 11f4acea4f2..b8531957f63 100644 +--- a/dlls/bcrypt/Makefile.in ++++ b/dlls/bcrypt/Makefile.in +@@ -2,7 +2,7 @@ MODULE = bcrypt.dll + IMPORTS = advapi32 + IMPORTLIB = bcrypt + UNIXLIB = bcrypt.so +-UNIX_CFLAGS = $(GNUTLS_CFLAGS) ++UNIX_CFLAGS = $(GNUTLS_CFLAGS) $(GMP_CFLAGS) + + C_SRCS = \ + bcrypt_main.c \ +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 4a9f223a838..d0cc55136e1 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -48,6 +48,15 @@ + + #include "wine/debug.h" + ++#include ++ ++#ifdef HAVE_GMP_H ++#include ++#endif ++ ++#include ++#include ++ + WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); + WINE_DECLARE_DEBUG_CHANNEL(winediag); + +@@ -127,8 +136,6 @@ static void dh_key_alloc( struct key *key ) + key_data(key)->d.pubkey = calloc( 1, dh_pubkey_len( key )); + } + +-static BOOL dh_supported; +- + /* Not present in gnutls version < 3.0 */ + static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t); + static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); +@@ -185,11 +192,9 @@ static int (*pgnutls_dh_params_import_raw2)(gnutls_dh_params_t dh_params, const + const gnutls_datum_t * generator, unsigned key_bits); + static int (*pgnutls_dh_params_export_raw)(gnutls_dh_params_t params, gnutls_datum_t * prime, + gnutls_datum_t * generator, unsigned int *bits); +-static int (*pgnutls_dh_generate_key)(gnutls_dh_params_t dh_params, gnutls_datum_t *priv_key, gnutls_datum_t *pub_key); +-static int (*pgnutls_dh_compute_key)(gnutls_dh_params_t dh_params, const gnutls_datum_t *priv_key, +- const gnutls_datum_t *pub_key, const gnutls_datum_t *peer_key, gnutls_datum_t *Z); + + static void *libgnutls_handle; ++ + #define MAKE_FUNCPTR(f) static typeof(f) * p##f + MAKE_FUNCPTR(gnutls_cipher_decrypt2); + MAKE_FUNCPTR(gnutls_cipher_deinit); +@@ -209,6 +214,22 @@ MAKE_FUNCPTR(gnutls_pubkey_deinit); + MAKE_FUNCPTR(gnutls_pubkey_encrypt_data); + MAKE_FUNCPTR(gnutls_pubkey_import_privkey); + MAKE_FUNCPTR(gnutls_pubkey_init); ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++static BOOL dh_supported; ++static void *libgmp_handle; ++ ++MAKE_FUNCPTR(mpz_init); ++MAKE_FUNCPTR(mpz_clear); ++MAKE_FUNCPTR(mpz_cmp); ++MAKE_FUNCPTR(_mpz_cmp_ui); ++MAKE_FUNCPTR(mpz_sizeinbase); ++MAKE_FUNCPTR(mpz_import); ++MAKE_FUNCPTR(mpz_export); ++MAKE_FUNCPTR(mpz_mod); ++MAKE_FUNCPTR(mpz_powm); ++MAKE_FUNCPTR(mpz_sub_ui); ++#endif + #undef MAKE_FUNCPTR + + static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size) +@@ -398,6 +419,37 @@ static NTSTATUS gnutls_process_attach( void *args ) + LOAD_FUNCPTR(gnutls_pubkey_init); + #undef LOAD_FUNCPTR + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++#define LOAD_FUNCPTR_STR(f) #f ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( libgmp_handle, LOAD_FUNCPTR_STR(f) ))) \ ++ { \ ++ ERR( "failed to load %s\n", LOAD_FUNCPTR_STR(f) ); \ ++ goto fail; \ ++ } ++ ++ if ((libgmp_handle = dlopen( SONAME_LIBGMP, RTLD_NOW ))) ++ { ++ LOAD_FUNCPTR(mpz_init); ++ LOAD_FUNCPTR(mpz_clear); ++ LOAD_FUNCPTR(mpz_cmp); ++ LOAD_FUNCPTR(_mpz_cmp_ui); ++ LOAD_FUNCPTR(mpz_sizeinbase); ++ LOAD_FUNCPTR(mpz_import); ++ LOAD_FUNCPTR(mpz_export); ++ LOAD_FUNCPTR(mpz_mod); ++ LOAD_FUNCPTR(mpz_powm); ++ LOAD_FUNCPTR(mpz_sub_ui); ++ } ++ else ++ { ++ ERR_(winediag)( "failed to load libgmp, no support for DH\n" ); ++ goto fail; ++ } ++#undef LOAD_FUNCPTR ++#undef LOAD_FUNCPTR_STR ++#endif ++ + #define LOAD_FUNCPTR_OPT(f) \ + if (!(p##f = dlsym( libgnutls_handle, #f ))) \ + { \ +@@ -457,19 +509,13 @@ static NTSTATUS gnutls_process_attach( void *args ) + { + WARN("gnutls_dh_params_export_raw not found\n"); + } +- if (!(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "_gnutls_dh_generate_key" )) +- && !(pgnutls_dh_generate_key = dlsym( libgnutls_handle, "gnutls_dh_generate_key" ))) +- { +- WARN("gnutls_dh_generate_key not found\n"); +- } +- if (!(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "_gnutls_dh_compute_key" )) +- && !(pgnutls_dh_compute_key = dlsym( libgnutls_handle, "gnutls_dh_compute_key" ))) +- { +- WARN("gnutls_dh_compute_key not found\n"); +- } + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) + dh_supported = pgnutls_dh_params_init && pgnutls_dh_params_generate2 && pgnutls_dh_params_import_raw2 +- && pgnutls_dh_generate_key && pgnutls_dh_compute_key; ++ && libgmp_handle; ++#else ++ ERR_(winediag)("Compiled without DH support.\n"); ++#endif + + if (TRACE_ON( bcrypt )) + { +@@ -482,6 +528,14 @@ static NTSTATUS gnutls_process_attach( void *args ) + fail: + dlclose( libgnutls_handle ); + libgnutls_handle = NULL; ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++ if (libgmp_handle) ++ { ++ dlclose( libgmp_handle ); ++ libgmp_handle = NULL; ++ } ++#endif + return STATUS_DLL_NOT_FOUND; + } + +@@ -494,6 +548,11 @@ static NTSTATUS gnutls_process_detach( void *args ) + libgnutls_handle = NULL; + } + return STATUS_SUCCESS; ++ ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++ dlclose( libgmp_handle ); ++ libgmp_handle = NULL; ++#endif + } + + struct buffer +@@ -1029,12 +1088,61 @@ static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l + return status; + } + ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) ++static NTSTATUS CDECL gen_random(void *buffer, unsigned int length) ++{ ++ unsigned int read_size; ++ int dev_random; ++ ++ dev_random = open("/dev/urandom", O_RDONLY); ++ if (dev_random == -1) ++ { ++ FIXME("couldn't open /dev/urandom.\n"); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ read_size = read(dev_random, buffer, length); ++ close(dev_random); ++ if (read_size != length) ++ { ++ FIXME("Could not read from /dev/urandom."); ++ return STATUS_INTERNAL_ERROR; ++ } ++ return STATUS_SUCCESS; ++} ++ ++static void import_mpz(mpz_t value, const void *input, unsigned int length) ++{ ++ pmpz_import(value, length, 1, 1, 0, 0, input); ++} ++ ++static void export_mpz(void *output, unsigned int length, const mpz_t value) ++{ ++ size_t export_length; ++ unsigned int offset; ++ ++ export_length = (pmpz_sizeinbase(value, 2) + 7) / 8; ++ assert(export_length <= length); ++ offset = length - export_length; ++ memset(output, 0, offset); ++ pmpz_export((BYTE *)output + offset, &export_length, 1, 1, 0, 0, value); ++ if (!export_length) ++ { ++ ERR("Zero export length, value bits %u.\n", (unsigned)pmpz_sizeinbase(value, 2)); ++ memset((BYTE *)output + offset, 0, length - offset); ++ } ++ else ++ { ++ assert(export_length + offset == length); ++ } ++} ++ + static NTSTATUS CDECL key_dh_generate( struct key *key ) + { +- gnutls_datum_t prime, generator, privkey, pubkey; + NTSTATUS status = STATUS_SUCCESS; +- gnutls_dh_params_t dh_params; ++ mpz_t p, psub1, g, privkey, pubkey; + ULONG key_length; ++ unsigned int i; + int ret; + + if (!dh_supported) +@@ -1043,22 +1151,18 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + return STATUS_NOT_IMPLEMENTED; + } + +- if ((ret = pgnutls_dh_params_init( &dh_params ))) +- { +- pgnutls_perror( ret ); +- return STATUS_INTERNAL_ERROR; +- } +- + key_length = key->u.a.bitlen / 8; + + if (!(key->u.a.flags & KEY_FLAG_DH_PARAMS_SET)) + { +- /* Generate parameters, export and then import them back below. +- * The bitlen in dh parameters (which is later used for keys generation) +- * is not set to gnutls_dh_params_generate2 'bits' parameter as one +- * could expect. gnutls_dh_params_generate2 generates 'q' (which is not +- * actually needed for DH) with the estimated bit length and then +- * sets the bit length to the 'q' bitlength. */ ++ gnutls_datum_t prime, generator; ++ gnutls_dh_params_t dh_params; ++ ++ if ((ret = pgnutls_dh_params_init( &dh_params ))) ++ { ++ pgnutls_perror( ret ); ++ return STATUS_INTERNAL_ERROR; ++ } + dh_key_alloc( key ); + + if ((ret = pgnutls_dh_params_generate2( dh_params, key->u.a.bitlen ))) +@@ -1073,6 +1177,8 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + pgnutls_dh_params_deinit( dh_params ); + return STATUS_INTERNAL_ERROR; + } ++ pgnutls_dh_params_deinit( dh_params ); ++ + + export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1), key_length, &prime, 1 ); + export_gnutls_datum( (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, +@@ -1083,34 +1189,73 @@ static NTSTATUS CDECL key_dh_generate( struct key *key ) + key->u.a.flags |= KEY_FLAG_DH_PARAMS_SET; + } + +- prime.size = generator.size = key_length; +- prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1); +- generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length; ++ pmpz_init(p); ++ pmpz_init(psub1); ++ pmpz_init(g); ++ pmpz_init(pubkey); ++ pmpz_init(privkey); + +- if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, key->u.a.bitlen ))) ++ import_mpz(p, (BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1, key_length); ++ if (!mpz_sgn(p)) + { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; ++ ERR("Got zero modulus.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; + } ++ pmpz_sub_ui(psub1, p, 1); + +- if ((ret = pgnutls_dh_generate_key( dh_params, &privkey, &pubkey ))) ++ import_mpz(g, (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + key_length, key_length); ++ if (!mpz_sgn(g)) + { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; ++ ERR("Got zero generator.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; + } ++ for (i = 0; i < 3; ++i) ++ { ++ if ((status = gen_random(key_data(key)->d.privkey, key_length))) ++ { ++ goto done; ++ } ++ import_mpz(privkey, key_data(key)->d.privkey, key_length); + +- export_gnutls_datum( (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, +- key_length, &pubkey, 1 ); +- export_gnutls_datum( key_data(key)->d.privkey, key_length, &privkey, 1); ++ pmpz_mod(privkey, privkey, p); ++ pmpz_powm(pubkey, g, privkey, p); ++ if (p_mpz_cmp_ui(pubkey, 1)) ++ break; ++ } ++ if (i == 3) ++ { ++ ERR("Could not generate key after 3 iterations.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } ++ ++ if (pmpz_cmp(pubkey, psub1) >= 0) ++ { ++ ERR("pubkey > p - 1.\n"); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } + +- free( privkey.data ); +- free( pubkey.data ); +- pgnutls_dh_params_deinit( dh_params ); ++ export_mpz(key_data(key)->d.privkey, key_length, privkey); ++ export_mpz((UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(key)->d.pubkey + 1) + 2 * key_length, key_length, pubkey); + ++done: ++ pmpz_clear(psub1); ++ pmpz_clear(p); ++ pmpz_clear(g); ++ pmpz_clear(pubkey); ++ pmpz_clear(privkey); + return status; + } ++#else ++static NTSTATUS CDECL key_dh_generate( struct key *key ) ++{ ++ ERR("Compiled without DH support.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++} ++#endif + + static NTSTATUS key_asymmetric_generate( void *args ) + { +@@ -2518,8 +2663,6 @@ static NTSTATUS key_secret_agreement( void *args ) + struct secret *secret; + struct key *priv_key; + struct key *peer_key; +- int ret; +- + priv_key = params->privkey; + peer_key = params->pubkey; + secret = params->secret; +@@ -2527,9 +2670,9 @@ static NTSTATUS key_secret_agreement( void *args ) + switch (priv_key->alg_id) + { + case ALG_ID_DH: ++#if defined(HAVE_GMP_H) && defined(SONAME_LIBGMP) + { +- gnutls_datum_t prime, generator, priv, peer, secret_datum; +- gnutls_dh_params_t dh_params; ++ mpz_t p, priv, peer, k; + ULONG key_length; + + if (!dh_supported) +@@ -2538,51 +2681,46 @@ static NTSTATUS key_secret_agreement( void *args ) + return STATUS_NOT_IMPLEMENTED; + } + +- if ((ret = pgnutls_dh_params_init( &dh_params ))) +- { +- pgnutls_perror( ret ); +- return STATUS_INTERNAL_ERROR; +- } +- + key_length = priv_key->u.a.bitlen / 8; + +- prime.size = generator.size = key_length; +- prime.data = (UCHAR *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1); +- generator.data = (BYTE *)((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1) + key_length; +- +- if ((ret = pgnutls_dh_params_import_raw2( dh_params, &prime, &generator, priv_key->u.a.bitlen ))) +- { +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); +- return STATUS_INTERNAL_ERROR; +- } +- +- priv.size = peer.size = key_length; +- priv.data = key_data(priv_key)->d.privkey; +- peer.data = key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2; +- + if (memcmp((BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, + key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB), key_length * 2)) + { + ERR("peer DH paramaters do not match.\n"); +- pgnutls_dh_params_deinit( dh_params ); + return STATUS_INTERNAL_ERROR; + } + +- if ((ret = pgnutls_dh_compute_key( dh_params, &priv, NULL, &peer, &secret_datum ))) ++ pmpz_init(p); ++ pmpz_init(priv); ++ pmpz_init(peer); ++ pmpz_init(k); ++ ++ import_mpz(p, (BCRYPT_DH_KEY_BLOB *)key_data(priv_key)->d.pubkey + 1, key_length); ++ if (pmpz_sizeinbase(p, 2) < 2) + { +- ERR("Error computing shared key.\n"); +- pgnutls_perror( ret ); +- pgnutls_dh_params_deinit( dh_params ); ++ ERR("Invalid prime.\n"); ++ pmpz_clear(p); ++ pmpz_clear(priv); ++ pmpz_clear(peer); ++ pmpz_clear(k); + return STATUS_INTERNAL_ERROR; + } +- +- TRACE("secret_datum.size %u, key_length %u.\n", secret_datum.size, (int)key_length); +- export_gnutls_datum( secret->data, key_length, &secret_datum, 1 ); ++ import_mpz(priv, key_data(priv_key)->d.privkey, key_length); ++ import_mpz(peer, key_data(peer_key)->d.pubkey + sizeof(BCRYPT_DH_KEY_BLOB) + key_length * 2, key_length); ++ pmpz_powm(k, peer, priv, p); ++ export_mpz(secret->data, key_length, k); + secret->data_len = key_length; +- free( secret_datum.data ); ++ ++ pmpz_clear(p); ++ pmpz_clear(priv); ++ pmpz_clear(peer); ++ pmpz_clear(k); + break; + } ++#else ++ ERR_(winediag)("Compiled without DH support.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++#endif + + case ALG_ID_ECDH_P256: + FIXME("ECDH is not supported.\n"); + +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 5d8c71c78c5..d32afe619ae 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -2754,7 +2754,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = + wow64_key_asymmetric_import + }; + +-C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count ); ++C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count-1 ); + + #endif /* _WIN64 */ \ No newline at end of file diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys2.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys2.mypatch new file mode 100644 index 000000000..2156fbe87 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys2.mypatch @@ -0,0 +1,341 @@ +From b1fb742375ee1b0dc0a785be39cf1faa667a87c7 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Tue, 8 Dec 2020 01:43:33 +0300 +Subject: [PATCH] bcrypt: Add support for calculating secret ecc keys. + +(updated by Paul Gofman) + +For Rainbow 6: Siege. +--- + configure.ac | 14 +++ + dlls/bcrypt/gnutls.c | 228 ++++++++++++++++++++++++++++++++++++- + dlls/bcrypt/tests/bcrypt.c | 11 +- + 3 files changed, 248 insertions(+), 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index c9bc5897f91..68bac283502 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -32,6 +32,7 @@ AC_ARG_WITH(dbus, AS_HELP_STRING([--without-dbus],[do not use DBus (dynamic + AC_ARG_WITH(float-abi, AS_HELP_STRING([--with-float-abi=abi],[specify the ABI (soft|softfp|hard) for ARM platforms])) + AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig])) + AC_ARG_WITH(freetype, AS_HELP_STRING([--without-freetype],[do not use the FreeType library])) ++AC_ARG_WITH(gcrypt, AS_HELP_STRING([--without-gcrypt],[do not use libgcrypt])) + AC_ARG_WITH(gettext, AS_HELP_STRING([--without-gettext],[do not use gettext])) + AC_ARG_WITH(gettextpo, AS_HELP_STRING([--with-gettextpo],[use the GetTextPO library to rebuild po files]), + [if test "x$withval" = "xno"; then ac_cv_header_gettext_po_h=no; fi]) +@@ -1810,6 +1811,19 @@ fi + WINE_NOTICE_WITH(vulkan,[test "x$ac_cv_lib_soname_vulkan" = "x" -a "x$ac_cv_lib_soname_MoltenVK" = "x"], + [libvulkan and libMoltenVK ${notice_platform}development files not found, Vulkan won't be supported.]) + ++dnl **** Check for gcrypt **** ++if test "x$with_gcrypt" != "xno" ++then ++ WINE_PACKAGE_FLAGS(GCRYPT,[libgcrypt],,,, ++ [AC_CHECK_HEADERS([gcrypt.h]) ++ if test "$ac_cv_header_gcrypt_h" = "yes" ++ then ++ WINE_CHECK_SONAME(gcrypt,gcry_sexp_build,,,[$GCRYPT_LIBS]) ++ fi]) ++fi ++WINE_NOTICE_WITH(gcrypt,[test "x$ac_cv_lib_soname_gcrypt" = "x"], ++ [libgcrypt ${notice_platform}development files not found, GCRYPT won't be supported.]) ++ + dnl **** Check for gcc specific options **** + + if test "x${GCC}" = "xyes" +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index d0cc55136e1..90c019672a2 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -57,6 +57,10 @@ + #include + #include + ++#ifdef HAVE_GCRYPT_H ++#include ++#endif ++ + WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); + WINE_DECLARE_DEBUG_CHANNEL(winediag); + +@@ -193,6 +197,12 @@ static int (*pgnutls_dh_params_import_raw2)(gnutls_dh_params_t dh_params, const + static int (*pgnutls_dh_params_export_raw)(gnutls_dh_params_t params, gnutls_datum_t * prime, + gnutls_datum_t * generator, unsigned int *bits); + ++static int (*pgnutls_ecdh_compute_key)(gnutls_ecc_curve_t curve, ++ const gnutls_datum_t *x, const gnutls_datum_t *y, ++ const gnutls_datum_t *k, ++ const gnutls_datum_t *peer_x, const gnutls_datum_t *peer_y, ++ gnutls_datum_t *Z); ++ + static void *libgnutls_handle; + + #define MAKE_FUNCPTR(f) static typeof(f) * p##f +@@ -230,6 +240,24 @@ MAKE_FUNCPTR(mpz_mod); + MAKE_FUNCPTR(mpz_powm); + MAKE_FUNCPTR(mpz_sub_ui); + #endif ++ ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++static BOOL gcrypt_available; ++static void *libgcrypt_handle; ++ ++MAKE_FUNCPTR(gcry_check_version); ++MAKE_FUNCPTR(gcry_sexp_build); ++MAKE_FUNCPTR(gcry_pk_encrypt); ++MAKE_FUNCPTR(gcry_mpi_new); ++MAKE_FUNCPTR(gcry_mpi_print); ++MAKE_FUNCPTR(gcry_sexp_release); ++MAKE_FUNCPTR(gcry_mpi_release); ++MAKE_FUNCPTR(gcry_strsource); ++MAKE_FUNCPTR(gcry_strerror); ++MAKE_FUNCPTR(gcry_sexp_find_token); ++MAKE_FUNCPTR(gcry_sexp_nth_mpi); ++#endif ++ + #undef MAKE_FUNCPTR + + static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size) +@@ -450,6 +478,36 @@ static NTSTATUS gnutls_process_attach( void *args ) + #undef LOAD_FUNCPTR_STR + #endif + ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( libgcrypt_handle, #f ))) \ ++ { \ ++ WARN( "failed to load %s\n", #f ); \ ++ gcrypt_available = FALSE; \ ++ } ++ ++ if ((libgcrypt_handle = dlopen( SONAME_LIBGCRYPT, RTLD_NOW ))) ++ { ++ gcrypt_available = TRUE; ++ ++ LOAD_FUNCPTR(gcry_check_version); ++ LOAD_FUNCPTR(gcry_sexp_build); ++ LOAD_FUNCPTR(gcry_pk_encrypt); ++ LOAD_FUNCPTR(gcry_mpi_new); ++ LOAD_FUNCPTR(gcry_mpi_print); ++ LOAD_FUNCPTR(gcry_sexp_release); ++ LOAD_FUNCPTR(gcry_mpi_release); ++ LOAD_FUNCPTR(gcry_strsource); ++ LOAD_FUNCPTR(gcry_strerror); ++ LOAD_FUNCPTR(gcry_sexp_find_token); ++ LOAD_FUNCPTR(gcry_sexp_nth_mpi); ++ } ++ else ++ WARN("failed to load gcrypt, no support for ECC secret agreement\n"); ++ ++#undef LOAD_FUNCPTR ++#endif ++ + #define LOAD_FUNCPTR_OPT(f) \ + if (!(p##f = dlsym( libgnutls_handle, #f ))) \ + { \ +@@ -517,6 +575,12 @@ static NTSTATUS gnutls_process_attach( void *args ) + ERR_(winediag)("Compiled without DH support.\n"); + #endif + ++ if (!(pgnutls_ecdh_compute_key = dlsym( libgnutls_handle, "_gnutls_ecdh_compute_key" )) ++ && !(pgnutls_ecdh_compute_key = dlsym( libgnutls_handle, "gnutls_ecdh_compute_key" ))) ++ { ++ WARN("gnutls_ecdh_compute_key not found\n"); ++ } ++ + if (TRACE_ON( bcrypt )) + { + pgnutls_global_set_log_level( 4 ); +@@ -553,6 +617,11 @@ static NTSTATUS gnutls_process_detach( void *args ) + dlclose( libgmp_handle ); + libgmp_handle = NULL; + #endif ++ ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++ dlclose( libgcrypt_handle ); ++ libgcrypt_handle = NULL; ++#endif + } + + struct buffer +@@ -2657,12 +2726,66 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) + return status; + } + ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++static NTSTATUS gcrypt_extract_result_into_secret(gcry_sexp_t result, struct secret *secret) ++{ ++ NTSTATUS status = STATUS_SUCCESS; ++ gcry_mpi_t fullcoords = NULL; ++ gcry_sexp_t fragment = NULL; ++ UCHAR *tmp_buffer = NULL; ++ gcry_error_t err; ++ size_t size; ++ ++ fragment = pgcry_sexp_find_token( result, "s", 0 ); ++ if (!fragment) ++ { ++ status = STATUS_NO_MEMORY; ++ goto done; ++ } ++ ++ fullcoords = pgcry_sexp_nth_mpi( fragment, 1, GCRYMPI_FMT_USG ); ++ if (!fullcoords) ++ { ++ status = STATUS_NO_MEMORY; ++ goto done; ++ } ++ ++ if ((err = pgcry_mpi_print( GCRYMPI_FMT_USG, NULL, 0, &size, fullcoords)) ) ++ { ++ ERR("Error = %s/%s.\n", pgcry_strsource( err ), pgcry_strerror( err )); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } ++ ++ tmp_buffer = malloc(size); ++ if ((err = pgcry_mpi_print( GCRYMPI_FMT_STD, tmp_buffer, size, NULL, fullcoords)) ) ++ { ++ ERR( "Error = %s/%s.\n", pgcry_strsource(err), pgcry_strerror(err) ); ++ status = STATUS_INTERNAL_ERROR; ++ goto done; ++ } ++ ++ memcpy( secret->data, tmp_buffer + size % 2, size / 2 ); ++ secret->data_len = size / 2; ++ ++done: ++ free( tmp_buffer ); ++ ++ pgcry_mpi_release( fullcoords ); ++ pgcry_sexp_release( fragment ); ++ ++ return status; ++} ++#endif ++ ++ + static NTSTATUS key_secret_agreement( void *args ) + { + struct key_secret_agreement_params *params = args; + struct secret *secret; + struct key *priv_key; + struct key *peer_key; ++ + priv_key = params->privkey; + peer_key = params->pubkey; + secret = params->secret; +@@ -2723,8 +2846,111 @@ static NTSTATUS key_secret_agreement( void *args ) + #endif + + case ALG_ID_ECDH_P256: +- FIXME("ECDH is not supported.\n"); ++ case ALG_ID_ECDH_P384: ++/* this is necessary since GNUTLS doesn't support ECDH public key encryption, maybe we can replace this when it does: ++ https://github.com/gnutls/gnutls/blob/cdc4fc288d87f91f974aa23b6e8595a53970ce00/lib/nettle/pk.c#L495 */ ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++ { ++ gcry_sexp_t xchg_result = NULL; ++ gcry_sexp_t privkey = NULL; ++ gcry_sexp_t pubkey = NULL; ++ const char *pubkey_format; ++ BCRYPT_ECCKEY_BLOB *h; ++ UCHAR *privkey_blob; ++ UCHAR *pubkey_raw; ++ gcry_error_t err; ++ ULONG key_length; ++ NTSTATUS status; ++ ULONG key_len; ++ ++ if (!gcrypt_available) ++ { ++ ERR("ECDH secret agreement is not available.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if (priv_key->alg_id == ALG_ID_ECDH_P256) ++ { ++ pubkey_format = "NIST P-256"; ++ key_length = 32; ++ } ++ else if (priv_key->alg_id == ALG_ID_ECDH_P384) ++ { ++ pubkey_format = "NIST P-384"; ++ key_length = 48; ++ } ++ else return STATUS_NOT_IMPLEMENTED; ++ ++ if (key_length != priv_key->u.a.bitlen / 8) ++ { ++ ERR( "Key length mismatch, key->u.a.bitlen %u, key_length %u.\n", (int)priv_key->u.a.bitlen, ++ (int)key_length ); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ if ((status = key_export_ecc( priv_key, NULL, 0, &key_len ))) ++ return status; ++ privkey_blob = malloc( key_len ); ++ if ((status = key_export_ecc( priv_key, privkey_blob, key_len, &key_len ))) ++ { ++ free( privkey_blob ); ++ return status; ++ } ++ ++ if ((status = key_export_ecc_public( peer_key, NULL, 0, &key_len ))) ++ return status; ++ h = malloc( key_len ); ++ if ((status = key_export_ecc_public( peer_key, (UCHAR *)h, key_len, &key_len ))) ++ { ++ free( privkey_blob ); ++ return status; ++ } ++ ++ /* copy public key into temporary buffer so we can prepend 0x04 (to indicate it is uncompressed) */ ++ pubkey_raw = malloc( (key_length * 2) + 1 ); ++ pubkey_raw[0] = 0x04; ++ memcpy( pubkey_raw + 1, h + 1, key_length * 2 ); ++ free( h ); ++ ++ err = pgcry_sexp_build( &pubkey, NULL, "(key-data(public-key(ecdh(curve %s)(q %b))))", pubkey_format, ++ (key_length * 2) + 1, pubkey_raw ); ++ free( pubkey_raw ); ++ if (err) ++ { ++ free( privkey_blob ); ++ ERR( "Failed to build gcrypt public key. err %s/%s\n", pgcry_strsource( err ), pgcry_strerror( err )); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ err = pgcry_sexp_build( &privkey, NULL, "(data(flags raw)(value %b))", key_length, ++ privkey_blob + sizeof(BCRYPT_ECCKEY_BLOB) + key_length * 2 ); ++ free( privkey_blob ); ++ if (err) ++ { ++ pgcry_sexp_release( pubkey ); ++ return STATUS_INTERNAL_ERROR; ++ } ++ err = pgcry_pk_encrypt( &xchg_result, privkey, pubkey ); ++ pgcry_sexp_release( privkey ); ++ pgcry_sexp_release( pubkey ); ++ if (err) ++ { ++ ERR( "Failed to perform key exchange. err %s/%s\n", pgcry_strsource( err ), pgcry_strerror( err )); ++ return STATUS_INTERNAL_ERROR; ++ } ++ status = gcrypt_extract_result_into_secret( xchg_result, secret ); ++ pgcry_sexp_release(xchg_result); ++ if (status) ++ { ++ ERR("Failed to extract secret key.\n"); ++ return status; ++ } + break; ++ } ++#else ++ WARN("Compiled without ECC secret support.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++#endif + + default: + ERR( "unhandled algorithm %u\n", priv_key->alg_id ); diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de3.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de3.mypatch new file mode 100644 index 000000000..fd02481e0 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de3.mypatch @@ -0,0 +1,263 @@ +From 1e2eb84a519573bf8b3671310cf4cd1d1526071a Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Fri, 1 Oct 2021 14:34:58 +0200 +Subject: [PATCH] bcrypt: Add support for OAEP-padded asymmetric key + decryption. + +(updated by Paul Gofman) + +For DayZ. + +CW-Bug-Id: #18973 +--- + dlls/bcrypt/bcrypt_internal.h | 2 + + dlls/bcrypt/bcrypt_main.c | 9 +- + dlls/bcrypt/gnutls.c | 166 ++++++++++++++++++++++++++++++++++ + dlls/bcrypt/tests/bcrypt.c | 9 +- + 4 files changed, 174 insertions(+), 12 deletions(-) + +diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h +index f0eaa6ad4fb..6dcf0c3d999 100644 +--- a/dlls/bcrypt/bcrypt_internal.h ++++ b/dlls/bcrypt/bcrypt_internal.h +@@ -253,6 +253,8 @@ struct key_asymmetric_encrypt_params + UCHAR *output; + ULONG output_len; + ULONG *ret_len; ++ void *padding; ++ ULONG flags; + }; + + struct key_asymmetric_duplicate_params +diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c +index bcb7a692ffd..af4b5321701 100644 +--- a/dlls/bcrypt/bcrypt_main.c ++++ b/dlls/bcrypt/bcrypt_main.c +@@ -715,7 +715,7 @@ static NTSTATUS get_rsa_property( enum chain_mode mode, const WCHAR *prop, UCHAR + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; +- if (buf) *(ULONG *)buf = BCRYPT_SUPPORTED_PAD_PKCS1_SIG; ++ if (buf) *(ULONG *)buf = BCRYPT_SUPPORTED_PAD_PKCS1_SIG | BCRYPT_SUPPORTED_PAD_OAEP; + return STATUS_SUCCESS; + } + +@@ -2138,11 +2138,6 @@ NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG inp + } + else + { +- if (flags & BCRYPT_PAD_NONE || flags & BCRYPT_PAD_OAEP) +- { +- FIXME( "flags %#lx not implemented\n", flags ); +- return STATUS_NOT_IMPLEMENTED; +- } + if (!is_asymmetric_encryption_key( key )) return STATUS_NOT_SUPPORTED; + + asymmetric_params.input = input; +@@ -2151,6 +2146,8 @@ NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG inp + asymmetric_params.output = output; + asymmetric_params.output_len = output_len; + asymmetric_params.ret_len = ret_len; ++ asymmetric_params.padding = padding; ++ asymmetric_params.flags = flags; + ret = UNIX_CALL(key_asymmetric_encrypt, &asymmetric_params); + } + +diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c +index 90c019672a2..fa48159cb6b 100644 +--- a/dlls/bcrypt/gnutls.c ++++ b/dlls/bcrypt/gnutls.c +@@ -256,6 +256,7 @@ MAKE_FUNCPTR(gcry_strsource); + MAKE_FUNCPTR(gcry_strerror); + MAKE_FUNCPTR(gcry_sexp_find_token); + MAKE_FUNCPTR(gcry_sexp_nth_mpi); ++MAKE_FUNCPTR(gcry_sexp_nth_data); + #endif + + #undef MAKE_FUNCPTR +@@ -501,6 +502,7 @@ static NTSTATUS gnutls_process_attach( void *args ) + LOAD_FUNCPTR(gcry_strerror); + LOAD_FUNCPTR(gcry_sexp_find_token); + LOAD_FUNCPTR(gcry_sexp_nth_mpi); ++ LOAD_FUNCPTR(gcry_sexp_nth_data); + } + else + WARN("failed to load gcrypt, no support for ECC secret agreement\n"); +@@ -2700,6 +2702,167 @@ static NTSTATUS key_asymmetric_decrypt( void *args ) + return status; + } + ++#if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) ++const char * gcrypt_hash_algorithm_name(LPCWSTR alg_id) ++{ ++ if (!wcscmp( alg_id, BCRYPT_SHA1_ALGORITHM )) return "sha1"; ++ if (!wcscmp( alg_id, BCRYPT_SHA256_ALGORITHM )) return "sha256"; ++ if (!wcscmp( alg_id, BCRYPT_SHA384_ALGORITHM )) return "sha384"; ++ if (!wcscmp( alg_id, BCRYPT_SHA512_ALGORITHM )) return "sha512"; ++ if (!wcscmp( alg_id, BCRYPT_MD2_ALGORITHM )) return "md2"; ++ if (!wcscmp( alg_id, BCRYPT_MD5_ALGORITHM )) return "md5"; ++ return NULL; ++} ++ ++static NTSTATUS key_asymmetric_encrypt_gcrypt( void *args ) ++{ ++ const struct key_asymmetric_encrypt_params *params = args; ++ struct key *key = params->key; ++ UCHAR *input = params->input; ++ ULONG input_len = params->input_len; ++ UCHAR *output = params->output; ++ ULONG *ret_len = params->ret_len; ++ void *padding = params->padding; ++ ULONG flags = params->flags; ++ BCRYPT_OAEP_PADDING_INFO *oaep_info = padding; ++ NTSTATUS status; ++ gcry_sexp_t sexp_pubkey = NULL; ++ gcry_sexp_t sexp_result = NULL; ++ gcry_sexp_t sexp_input = NULL; ++ BCRYPT_RSAKEY_BLOB *rsa_blob; ++ gcry_sexp_t mpi_a = NULL; ++ const void *result; ++ size_t result_len; ++ gcry_error_t err; ++ ULONG len; ++ ++ if (!gcrypt_available) ++ { ++ ERR("Asymmetric encryption not available.\n"); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if (key->alg_id != ALG_ID_RSA) ++ { ++ FIXME("Unsupported algorithm id: %u\n", key->alg_id); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ if (flags == BCRYPT_PAD_NONE && input_len != key->u.a.bitlen / 8) ++ { ++ WARN( "Invalid input_len %u for BCRYPT_PAD_NONE.\n", (int)input_len ); ++ return STATUS_INVALID_PARAMETER; ++ } ++ ++ /* import RSA key */ ++ if ((status = key_export_rsa_public( key, NULL, 0, &len ))) ++ { ++ ERR( "Key export failed.\n" ); ++ return status; ++ } ++ rsa_blob = malloc( len ); ++ if ((status = key_export_rsa_public( key, (UCHAR *)rsa_blob, len, &len ))) ++ { ++ ERR( "Key export failed.\n" ); ++ return status; ++ } ++ err = pgcry_sexp_build(&sexp_pubkey, NULL, ++ "(public-key(rsa (e %b)(n %b)))", ++ rsa_blob->cbPublicExp, ++ (UCHAR *)(rsa_blob + 1), ++ rsa_blob->cbModulus, ++ (UCHAR *)(rsa_blob + 1) + rsa_blob->cbPublicExp); ++ free( rsa_blob ); ++ if (err) ++ { ++ ERR("Failed to build gcrypt public key\n"); ++ goto done; ++ } ++ ++ /* import input data with necessary padding */ ++ if (flags == BCRYPT_PAD_PKCS1) ++ { ++ err = pgcry_sexp_build(&sexp_input, NULL, ++ "(data(flags pkcs1)(value %b))", ++ input_len, ++ input); ++ } ++ else if (flags == BCRYPT_PAD_OAEP) ++ { ++ if (oaep_info->pbLabel) ++ err = pgcry_sexp_build(&sexp_input, NULL, ++ "(data(flags oaep)(hash-algo %s)(label %b)(value %b))", ++ gcrypt_hash_algorithm_name(oaep_info->pszAlgId), ++ oaep_info->cbLabel, ++ oaep_info->pbLabel, ++ input_len, ++ input); ++ else ++ err = pgcry_sexp_build(&sexp_input, NULL, ++ "(data(flags oaep)(hash-algo %s)(value %b))", ++ gcrypt_hash_algorithm_name(oaep_info->pszAlgId), ++ input_len, ++ input); ++ } ++ else if (flags == BCRYPT_PAD_NONE) ++ { ++ err = pgcry_sexp_build(&sexp_input, NULL, ++ "(data(flags raw)(value %b))", ++ input_len, ++ input); ++ } ++ else ++ { ++ status = STATUS_INVALID_PARAMETER; ++ goto done; ++ } ++ ++ if (err) ++ { ++ ERR("Failed to build gcrypt padded input data\n"); ++ goto done; ++ } ++ ++ if ((err = pgcry_pk_encrypt(&sexp_result, sexp_input, sexp_pubkey))) ++ { ++ ERR("Failed to encrypt data\n"); ++ goto done; ++ } ++ ++ mpi_a = pgcry_sexp_find_token(sexp_result, "a", 0); ++ result = pgcry_sexp_nth_data(mpi_a, 1, &result_len); ++ ++ *ret_len = result_len; ++ ++ if (params->output_len >= result_len) memcpy(output, result, result_len); ++ else if (params->output_len == 0) status = STATUS_SUCCESS; ++ else status = STATUS_BUFFER_TOO_SMALL; ++ ++done: ++ pgcry_sexp_release(sexp_input); ++ pgcry_sexp_release(sexp_pubkey); ++ pgcry_sexp_release(sexp_result); ++ pgcry_sexp_release(mpi_a); ++ ++ if (status) ++ return status; ++ ++ if (err) ++ { ++ ERR("Error = %s/%s\n", pgcry_strsource (err), pgcry_strerror (err)); ++ return STATUS_INTERNAL_ERROR; ++ } ++ ++ return STATUS_SUCCESS; ++} ++#else ++static NTSTATUS key_asymmetric_encrypt_gcrypt( void *args ) ++{ ++ ERR("Asymmetric key encryption not supported without gcrypt.\n"); ++ return STATUS_NOT_IMPLEMENTED; ++} ++#endif ++ + static NTSTATUS key_asymmetric_encrypt( void *args ) + { + const struct key_asymmetric_encrypt_params *params = args; +@@ -2709,6 +2872,9 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) + + if (!key_data(params->key)->a.pubkey) return STATUS_INVALID_HANDLE; + ++ if (params->flags == BCRYPT_PAD_NONE || params->flags == BCRYPT_PAD_OAEP) ++ return key_asymmetric_encrypt_gcrypt( args ); ++ + d.data = params->input; + d.size = params->input_len; + if ((ret = pgnutls_pubkey_encrypt_data(key_data(params->key)->a.pubkey, 0, &d, &e))) diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/hotfixes index 0e0c0e021..ae191e89c 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/rdr2/hotfixes @@ -2,30 +2,53 @@ # bcrypt fixes for RDR2 if [ "$_use_staging" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 2ad44002da683634de768dbe49a0ba09c5f26f08 HEAD ); then - if [ "$_NOLIB32" != "true" ] && [ "$_proton_bcrypt" = "true" ] && ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor 37e000145f07c4ec6f48fdac5969bbbb05435d52 HEAD ) && grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" && ! grep -Fxq 'Disabled: True' "${srcdir}/${_stgsrcdir}/patches/ntdll-Syscall_Emulation/definition"; then + if [ "$_NOLIB32" != "true" ] && [ "$_proton_bcrypt" = "true" ] && ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor 37e000145f07c4ec6f48fdac5969bbbb05435d52 HEAD ) && ( [ ! -e "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ] || ( [ -e "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ] && grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ) ) && ! grep -Fxq 'Disabled: True' "${srcdir}/${_stgsrcdir}/patches/ntdll-Syscall_Emulation/definition"; then warning "Hotfix: Bcrypt fixes for RDR2" - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ef6e33f89f94e1fc109bb6c415d7c80f141619d5 HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/ef6e33f) - else - _hotfix_mainlinereverts+=(341cb1a933aec7b2858414f571ea98ba29caa72a) + + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor be9f66c62bc6f168e3fb41682391b0b37c999047 HEAD && ! git merge-base --is-ancestor 7231d4586d3ceccdf46f13bdd696d2b3ce0dc675 HEAD ); then + _hotfix_mainlinereverts+=(8f087c4dff65d79f545f575cae39ffa0da5056bd be9f66c62bc6f168e3fb41682391b0b37c999047 36e1c9380c330b35e32d9ac1a59d122abee2a5f9 94d90ceec4d3ff37265c5bb1eb23ad91b817c9b0 df83abb24151764ef17eaf9b99b19b4e0b52af49 81d71ee0bb07bb38f3c3797399f8651c27d7440a cfa655c4a32ac2e2d43aaebef4f3127621a92f78) + fi + + if ! ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 68a3b0077e64d1b5232ff75996b82766bcc64ced HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ef6e33f89f94e1fc109bb6c415d7c80f141619d5 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/ef6e33f) + else + _hotfix_mainlinereverts+=(341cb1a933aec7b2858414f571ea98ba29caa72a) + fi fi - _hotfix_mainlinereverts+=(533454111c88bef008cef0296f61da4dcf9e1ebc 3fb2a5d55e948670222170075f0054a2aabf8d7e a7efc770f7b1e1da959367e3186d871dce50c6e2 6f455e10afaf95f64008f45cdb5b794308275504 e1fb63168756c73ebd97a8c7f3b3aba00c52e444 a88cc503653c06b612429d98e4e6174278687b94 ccdef80543ff5c1f08a8aaff39c3f623522152df 45b823114ce89ee56a36705a77f4f6bb9761dde4 96692a2c21583c50834d368d86e83511115474dd 5308911a82e5858f419354bcc6d9e35807134bc4 4c46bd922d286ed14442c548c53710613c5067d7 f95c2e4629f6cb15eabc19d71ccbe17c0758cd5d ffad823e34f1f940530f72b7e17583da22d39d93 cb3cc2b59f47842fa8e46115354d8b4f446d2f5c 11cc3ca15122c31fd5545372d749a6dafb0e7c60 a4f8d28de77148f7e8a5ad0fe9376afaabb44d49 d75f651404028baa23d2591edf222837a167f790 d1291ebd4030c3ecc2f30199b963015077ab218c ec85dfe3ab9ba56fbe8b3c30ef393d470e292a36 6f5028dd03eba2cfffc8a844e482040d4ca29655 6db0fc5ce8f556ca9a192a5f9167a3045338c9b0 ba1631ad885cd12034589fb60b1d90d8007ab3bb c7e0f923bc706b57dcb4a54c15733cc1acc12b07 7780caf4ee5747499643c511d612065756429cb7 430b9db051c0566424b0becee351591b6056894b c7b5778d83c688dd973efe503ad8646004e69296 4918be2c5905c849cc992e0968858365401c4a8e c6a75d01b52973ed0763ee1070634a2f2d111350 fcb553ffc2caec728381fc577f1774432d3a969e 5253c8d77e8e7dc5770981e97e67348f393b99b9 dd61c5638a0e597a488ca74e48f1ac97bc008f93 48075d2a0874d368e617632bd5e875e4bc18c411 1fa5bfd7dab270113c8fd2b48c838396719c639b 5b860a44a0051cf779ec3391610a807e711d184f 00dfa1bd04ae7f93ee7dd4235dd2439697efd318 777cbf06d2e8db298313982b7823acbc40dc05db b352d353b41654ad2b94d46747c5d203acc417f3 0c2408464cfea0d3e5b073c56ac0d264d1abe576 c1ed9ca9b18267b2139fcf7fd68f24d2c6d7a2a3 9176251af425ba494c0334c5659aa47941ee7c85 f3d4df60ff4afbf1983387a098ae96830384f53a) - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 194e09baeca42b369b391d87c9605338b8fbc978 HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes5) - elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 045f50a78a210cc16e25ebffa276003ae69e1994 HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes4) - elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ab4edfd8621d4811ce9a74d066eef0904a45a74f HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes3) - elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor fcc827d141f32954545b349f06b01a3909b95ac0 HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes2) - else - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes) + if ! ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 68a3b0077e64d1b5232ff75996b82766bcc64ced HEAD ); then + _hotfix_mainlinereverts+=(533454111c88bef008cef0296f61da4dcf9e1ebc 3fb2a5d55e948670222170075f0054a2aabf8d7e a7efc770f7b1e1da959367e3186d871dce50c6e2 6f455e10afaf95f64008f45cdb5b794308275504 e1fb63168756c73ebd97a8c7f3b3aba00c52e444 a88cc503653c06b612429d98e4e6174278687b94 ccdef80543ff5c1f08a8aaff39c3f623522152df 45b823114ce89ee56a36705a77f4f6bb9761dde4 96692a2c21583c50834d368d86e83511115474dd 5308911a82e5858f419354bcc6d9e35807134bc4 4c46bd922d286ed14442c548c53710613c5067d7 f95c2e4629f6cb15eabc19d71ccbe17c0758cd5d ffad823e34f1f940530f72b7e17583da22d39d93 cb3cc2b59f47842fa8e46115354d8b4f446d2f5c 11cc3ca15122c31fd5545372d749a6dafb0e7c60 a4f8d28de77148f7e8a5ad0fe9376afaabb44d49 d75f651404028baa23d2591edf222837a167f790 d1291ebd4030c3ecc2f30199b963015077ab218c ec85dfe3ab9ba56fbe8b3c30ef393d470e292a36 6f5028dd03eba2cfffc8a844e482040d4ca29655 6db0fc5ce8f556ca9a192a5f9167a3045338c9b0 ba1631ad885cd12034589fb60b1d90d8007ab3bb c7e0f923bc706b57dcb4a54c15733cc1acc12b07 7780caf4ee5747499643c511d612065756429cb7 430b9db051c0566424b0becee351591b6056894b c7b5778d83c688dd973efe503ad8646004e69296 4918be2c5905c849cc992e0968858365401c4a8e c6a75d01b52973ed0763ee1070634a2f2d111350 fcb553ffc2caec728381fc577f1774432d3a969e 5253c8d77e8e7dc5770981e97e67348f393b99b9 dd61c5638a0e597a488ca74e48f1ac97bc008f93 48075d2a0874d368e617632bd5e875e4bc18c411 1fa5bfd7dab270113c8fd2b48c838396719c639b 5b860a44a0051cf779ec3391610a807e711d184f 00dfa1bd04ae7f93ee7dd4235dd2439697efd318 777cbf06d2e8db298313982b7823acbc40dc05db b352d353b41654ad2b94d46747c5d203acc417f3 0c2408464cfea0d3e5b073c56ac0d264d1abe576 c1ed9ca9b18267b2139fcf7fd68f24d2c6d7a2a3 9176251af425ba494c0334c5659aa47941ee7c85 f3d4df60ff4afbf1983387a098ae96830384f53a) fi - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys) - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 39336fd02d90b8d7d1d24d5eb31f9b0172b60a17 HEAD ); then - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de2) - else - _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de) + if ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor 7231d4586d3ceccdf46f13bdd696d2b3ce0dc675 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 2fd33f6ce10f67cfe553f6bac9a8a853c997f9ab HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes7) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 68a3b0077e64d1b5232ff75996b82766bcc64ced HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes6) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 194e09baeca42b369b391d87c9605338b8fbc978 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes5) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 045f50a78a210cc16e25ebffa276003ae69e1994 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes4) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ab4edfd8621d4811ce9a74d066eef0904a45a74f HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes3) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor fcc827d141f32954545b349f06b01a3909b95ac0 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes2) + else + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0001-proton-bcrypt_rdr2_fixes) + fi + + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 68a3b0077e64d1b5232ff75996b82766bcc64ced HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys2) + else + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0002-bcrypt-Add-support-for-calculating-secret-ecc-keys) + fi + + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 68a3b0077e64d1b5232ff75996b82766bcc64ced HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de3) + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 39336fd02d90b8d7d1d24d5eb31f9b0172b60a17 HEAD ); then + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de2) + else + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/rdr2/0003-bcrypt-Add-support-for-OAEP-padded-asymmetric-key-de) + fi fi fi fi diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/hotfixes new file mode 100644 index 000000000..8fd2e948b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/hotfixes @@ -0,0 +1,8 @@ +#!/bin/bash + +# Following upstream commit acad49573a7846c0199ea3a8f1bd11c42d647ff4 +if [ "$_use_staging" = "true" ] && ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor aeddc191a7a21996ad8d2176247e850f3e3df798 HEAD ); then + warning "Hotfix: Fix for staging d3d12core" + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_d3d12core) + _hotfixes+=("$_where"/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_windows.networking.connectivity) +fi diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_d3d12core.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_d3d12core.mypatch new file mode 100644 index 000000000..e55d93622 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_d3d12core.mypatch @@ -0,0 +1,16 @@ +From d204d711713913e138e49fdb1bbaf2254ab99b8e Mon Sep 17 00:00:00 2001 +From: Etienne JUVIGNY +Date: Fri, 10 Nov 2023 03:06:33 +0100 +Subject: Always use the global SOURCES variable for .c files + + +diff --git a/dlls/d3d12core/Makefile.in b/dlls/d3d12core/Makefile.in +index 94d8fd4da00..44f85971ddd 100644 +--- a/dlls/d3d12core/Makefile.in ++++ b/dlls/d3d12core/Makefile.in +@@ -1,4 +1,4 @@ + MODULE = d3d12core.dll + +-C_SRCS = \ ++SOURCES = \ + d3d12core_main.c diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_windows.networking.connectivity.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_windows.networking.connectivity.mypatch new file mode 100644 index 000000000..2348395c5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/staging_aeddc19_SOURCES/staging_windows.networking.connectivity.mypatch @@ -0,0 +1,17 @@ +From c1d610ba7e871acba68f47edc464b45c8b874bcc Mon Sep 17 00:00:00 2001 +From: Etienne JUVIGNY +Date: Fri, 10 Nov 2023 03:39:08 +0100 +Subject: Always use the global SOURCES variable for .c files + + +diff --git a/dlls/windows.networking.connectivity/Makefile.in b/dlls/windows.networking.connectivity/Makefile.in +index 6fc24a72feb..b5a57c13128 100644 +--- a/dlls/windows.networking.connectivity/Makefile.in ++++ b/dlls/windows.networking.connectivity/Makefile.in +@@ -3,5 +3,5 @@ IMPORTS = combase uuid + + EXTRADLLFLAGS = -mno-cygwin + +-C_SRCS = \ ++SOURCES = \ + windows.networking.connectivity_main.c diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-be.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-be.mypatch new file mode 100644 index 000000000..9afe97004 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-be.mypatch @@ -0,0 +1,801 @@ +From 25a2de878c9fcaad2c2d6c3935cf368552e5ff6d Mon Sep 17 00:00:00 2001 +From: kotarac +Date: Wed, 24 May 2023 08:30:51 +0200 +Subject: [PATCH] de-steamify + +--- + dlls/advapi32/advapi.c | 34 ++++---- + dlls/dbghelp/dwarf.c | 5 -- + dlls/hidclass.sys/device.c | 7 +- + dlls/hidclass.sys/hid.h | 6 -- + dlls/hidclass.sys/pnp.c | 12 --- + dlls/kernelbase/process.c | 15 ---- + dlls/ntdll/loader.c | 2 +- + dlls/ntdll/unix/loader.c | 59 +------------- + dlls/secur32/secur32.c | 15 +--- + dlls/winebus.sys/bus_sdl.c | 6 +- + dlls/winex11.drv/window.c | 15 +--- + dlls/xinput1_3/main.c | 146 +++++++++++++--------------------- + loader/wine.inf.in | 1 - + programs/winedbg/debugger.h | 1 - + programs/winedbg/tgt_active.c | 45 ----------- + programs/winedbg/winedbg.c | 9 +-- + 16 files changed, 87 insertions(+), 291 deletions(-) + +diff --git a/dlls/advapi32/advapi.c b/dlls/advapi32/advapi.c +index a22e896c88a..6b3ffe2ea25 100644 +--- a/dlls/advapi32/advapi.c ++++ b/dlls/advapi32/advapi.c +@@ -44,15 +44,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(advapi); + */ + BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + { +- static const char steamuserA[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserA)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserA); +- return FALSE; +- } +- memcpy(name, steamuserA, sizeof(steamuserA)); +- *size = ARRAY_SIZE(steamuserA); +- return TRUE; ++ DWORD len = GetEnvironmentVariableA( "WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +@@ -60,15 +59,14 @@ BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + */ + BOOL WINAPI GetUserNameW( LPWSTR name, LPDWORD size ) + { +- static const WCHAR steamuserW[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserW)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserW); +- return FALSE; +- } +- memcpy(name, steamuserW, sizeof(steamuserW)); +- *size = ARRAY_SIZE(steamuserW); +- return TRUE; ++ DWORD len = GetEnvironmentVariableW( L"WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c +index a2d57173587..9ed63463513 100644 +--- a/dlls/dbghelp/dwarf.c ++++ b/dlls/dbghelp/dwarf.c +@@ -4200,11 +4200,6 @@ BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, + struct module_format* dwarf2_modfmt; + dwarf2_parse_module_context_t module_ctx; + +-/* Our DWARF parser has been known to crash winedbg in some cases. Since +- * probably no concerned parties are going to be using plain winedbg, just don't +- * bother parsing anything. */ +-return FALSE; +- + if (!dwarf2_init_section(&eh_frame, fmap, ".eh_frame", NULL, &eh_frame_sect)) + /* lld produces .eh_fram to avoid generating a long name */ + dwarf2_init_section(&eh_frame, fmap, ".eh_fram", NULL, &eh_frame_sect); +diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c +index 2092054d8ca..ac201afeddf 100644 +--- a/dlls/hidclass.sys/device.c ++++ b/dlls/hidclass.sys/device.c +@@ -223,7 +223,6 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + const BOOL polled = ext->u.pdo.information.Polled; + ULONG size, report_len = polled ? packet->reportBufferLen : desc->InputLength; + struct hid_report *last_report, *report; +- BOOL steam_overlay_open = FALSE; + struct hid_queue *queue; + LIST_ENTRY completed, *entry; + RAWINPUT *rawinput; +@@ -232,11 +231,7 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + + TRACE("device %p, packet %p\n", device, packet); + +- if (WaitForSingleObject(ext->steam_overlay_event, 0) == WAIT_OBJECT_0 || /* steam overlay is open */ +- WaitForSingleObject(ext->steam_keyboard_event, 0) == WAIT_OBJECT_0) /* steam keyboard is open */ +- steam_overlay_open = TRUE; +- +- if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID ) && !steam_overlay_open) ++ if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID )) + { + size = offsetof( RAWINPUT, data.hid.bRawData[report_len] ); + if (!(rawinput = malloc( size ))) ERR( "Failed to allocate rawinput data!\n" ); +diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h +index c14f8d7a942..b8cec55fb7c 100644 +--- a/dlls/hidclass.sys/hid.h ++++ b/dlls/hidclass.sys/hid.h +@@ -84,9 +84,6 @@ typedef struct _BASE_DEVICE_EXTENSION + WCHAR container_id[MAX_GUID_STRING_LEN]; + const GUID *class_guid; + +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; +- + BOOL is_fdo; + } BASE_DEVICE_EXTENSION; + +@@ -118,9 +115,6 @@ typedef struct _minidriver + + PDRIVER_ADD_DEVICE AddDevice; + PDRIVER_DISPATCH PNPDispatch; +- +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; + } minidriver; + + void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in_size, +diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c +index ca367ec3a7c..c0e2a874788 100644 +--- a/dlls/hidclass.sys/pnp.c ++++ b/dlls/hidclass.sys/pnp.c +@@ -178,9 +178,6 @@ static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *b + if (get_device_id(bus_pdo, BusQueryContainerID, ext->container_id)) + ext->container_id[0] = 0; + +- ext->steam_overlay_event = minidriver->steam_overlay_event; +- ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + is_xinput_class = !wcsncmp(device_id, L"WINEXINPUT\\", 7) && wcsstr(device_id, L"&XI_") != NULL; + if (is_xinput_class) ext->class_guid = &GUID_DEVINTERFACE_WINEXINPUT; + else ext->class_guid = &GUID_DEVINTERFACE_HID; +@@ -245,9 +242,6 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) + pdo_ext->u.pdo.information.VersionNumber = attr.VersionNumber; + pdo_ext->u.pdo.information.Polled = minidriver->minidriver.DevicesArePolled; + +- pdo_ext->steam_overlay_event = minidriver->steam_overlay_event; +- pdo_ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + call_minidriver( IOCTL_HID_GET_DEVICE_DESCRIPTOR, fdo, NULL, 0, &descriptor, sizeof(descriptor), &io ); + if (io.Status != STATUS_SUCCESS) + { +@@ -597,9 +591,6 @@ static void WINAPI driver_unload(DRIVER_OBJECT *driver) + if (md->DriverUnload) + md->DriverUnload(md->minidriver.DriverObject); + list_remove(&md->entry); +- +- CloseHandle(md->steam_overlay_event); +- CloseHandle(md->steam_keyboard_event); + free(md); + } + } +@@ -615,9 +606,6 @@ NTSTATUS WINAPI HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION *registration) + if (!(driver = calloc(1, sizeof(*driver)))) + return STATUS_NO_MEMORY; + +- driver->steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- driver->steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + driver->DriverUnload = registration->DriverObject->DriverUnload; + registration->DriverObject->DriverUnload = driver_unload; + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 24c43357a17..d71d0780044 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -1223,21 +1223,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH OpenProcess( DWORD access, BOOL inherit, DWORD i + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + +- /* PROTON HACK: +- * On Windows, the Steam client puts its process ID into the registry +- * at: +- * +- * [HKCU\Software\Valve\Steam\ActiveProcess] +- * PID=dword:00000008 +- * +- * Games get that pid from the registry and then query it with +- * OpenProcess to ensure Steam is running. Since we aren't running the +- * Windows Steam in Wine, instead we hack this magic number into the +- * registry and then substitute the game's process itself in its place +- * so it can query a valid process. +- */ +- if (id == 0xfffe) id = GetCurrentProcessId(); +- + cid.UniqueProcess = ULongToHandle(id); + cid.UniqueThread = 0; + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 9bd07a9bec7..a827e4e47c1 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -86,7 +86,7 @@ const WCHAR windows_dir[] = L"C:\\windows"; + const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index d3b086f2731..948a46fa70c 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -773,46 +773,13 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; +- const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; + if (pe_info->image_flags & IMAGE_FLAGS_ComPlusNativeReady) machine = native_machine; + + unsetenv( "WINE_LD_PRELOAD" ); + +- /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ +- if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && +- argv[3] && !strcmp( argv[3], "/desktop" )) +- { +- static char const gorso[] = "gameoverlayrenderer.so"; +- static int gorso_len = sizeof(gorso) - 1; +- int len = strlen( ld_preload ); +- char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); +- +- if (!env) return STATUS_NO_MEMORY; +- strcpy( env, "LD_PRELOAD=" ); +- strcat( env, ld_preload ); +- +- tmp = env + 11; +- do +- { +- if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); +- if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) +- { +- if (*next) memmove( tmp, next + 1, strlen(next) ); +- else *tmp = 0; +- next = tmp; +- } +- else tmp = next + 1; +- } +- while (*next); +- +- putenv( env ); +- ld_preload = NULL; +- } +- +- if (ld_preload) setenv( "WINE_LD_PRELOAD", ld_preload, 1 ); + + signal( SIGPIPE, SIG_DFL ); + +@@ -2140,22 +2109,8 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + +-static void steamclient_write_jump(void *src_addr, void *tgt_addr) +-{ +-#ifdef _WIN64 +- static const char mov[] = {0x48, 0xb8}; +-#else +- static const char mov[] = {0xb8}; +-#endif +- static const char jmp[] = {0xff, 0xe0}; +- memcpy(src_addr, mov, sizeof(mov)); +- memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); +- memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); +-} +- + static NTSTATUS steamclient_setup_trampolines( void *args ) + { +- static int noexec_cached = -1; + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; +@@ -2166,13 +2121,10 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name, *wsne; ++ char *name; + UINT_PTR page_mask; + int i; + +- if (noexec_cached == -1) +- noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); +- + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2181,8 +2133,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- if (noexec_cached) mprotect(addr, size, PROT_READ); +- else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); ++ mprotect(addr, size, PROT_READ); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2196,8 +2147,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2205,8 +2155,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + + return STATUS_SUCCESS; + } +diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c +index bf94b1a594e..d703757bae7 100644 +--- a/dlls/secur32/secur32.c ++++ b/dlls/secur32/secur32.c +@@ -1135,22 +1135,9 @@ BOOLEAN WINAPI GetUserNameExW( + return FALSE; + } + +- case NameDisplay: +- { +- static const WCHAR wineusernameW[] = {'W','I','N','E','U','S','E','R','N','A','M','E',0}; +- +- DWORD needed = GetEnvironmentVariableW(wineusernameW, NULL, 0); +- if (*nSize < needed) { +- *nSize = needed; +- SetLastError(ERROR_MORE_DATA); +- return FALSE; +- } +- *nSize = GetEnvironmentVariableW(wineusernameW, lpNameBuffer, *nSize); +- return TRUE; +- } +- + case NameUnknown: + case NameFullyQualifiedDN: ++ case NameDisplay: + case NameUniqueId: + case NameCanonical: + case NameUserPrincipal: +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index f4414ae1f50..4ed55ce9c01 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -965,8 +965,7 @@ static void sdl_add_device(unsigned int index) + SDL_JoystickID id; + SDL_JoystickType joystick_type; + SDL_GameController *controller = NULL; +- const char *product, *sdl_serial, *str, *env; ++ const char *product, *sdl_serial; +- BOOL expose_steam_controller = (env = getenv("PROTON_EXPOSE_STEAM_CONTROLLER")) && atoi(env) == 1; + char guid_str[33], buffer[ARRAY_SIZE(desc.product)]; + int axis_count, axis_offset; + +@@ -1021,21 +1021,13 @@ static void sdl_add_device(unsigned int index) + return; + } + +- if (desc.vid == 0x28de && desc.pid == 0x11ff && !expose_steam_controller) ++ if (desc.vid == 0x28de && desc.pid == 0x11ff) + { + TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); + desc.vid = 0x045e; + desc.pid = 0x028e; + } + +- /* CW-Bug-Id: #20528 Check steam virtual controller indexes to keep them ordered */ +- /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ +- if ((str = pSDL_JoystickName(joystick)) && sscanf(str, "Microsoft X-Box 360 pad %u", &desc.input) == 1) +- { +- if (!expose_steam_controller) desc.input++; +- desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ +- } +- + if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) + { + ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index a502ea9166f..adcc82e4f7c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1056,19 +1056,8 @@ static void set_initial_wm_hints( Display *display, Window window ) + /* class hints */ + if ((class_hints = XAllocClassHint())) + { +- static char steam_proton[] = "steam_proton"; +- const char *app_id = getenv("SteamAppId"); +- char proton_app_class[128]; +- +- if(app_id && *app_id){ +- snprintf(proton_app_class, sizeof(proton_app_class), "steam_app_%s", app_id); +- class_hints->res_name = proton_app_class; +- class_hints->res_class = proton_app_class; +- }else{ +- class_hints->res_name = steam_proton; +- class_hints->res_class = steam_proton; +- } +- ++ class_hints->res_name = process_name; ++ class_hints->res_class = process_name; + XSetClassHint( display, window, class_hints ); + XFree( class_hints ); + } +diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c +index 192d75413fa..2abf33a8d45 100644 +--- a/dlls/xinput1_3/main.c ++++ b/dlls/xinput1_3/main.c +@@ -124,8 +124,19 @@ static HANDLE start_event; + static HANDLE stop_event; + static HANDLE done_event; + static HANDLE update_event; +-static HANDLE steam_overlay_event; +-static HANDLE steam_keyboard_event; ++ ++static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) ++{ ++ int i; ++ ++ *free_slot = XUSER_MAX_COUNT; ++ for (i = XUSER_MAX_COUNT; i > 0; i--) ++ { ++ if (!controllers[i - 1].device) *free_slot = i - 1; ++ else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; ++ } ++ return FALSE; ++} + + static void check_value_caps(struct xinput_controller *controller, USHORT usage, HIDP_VALUE_CAPS *caps) + { +@@ -328,40 +339,7 @@ static DWORD HID_set_state(struct xinput_controller *controller, XINPUT_VIBRATIO + return ERROR_SUCCESS; + } + +-static void controller_disable(struct xinput_controller *controller) +-{ +- XINPUT_VIBRATION state = {0}; +- +- if (!controller->enabled) return; +- if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); +- controller->enabled = FALSE; +- +- CancelIoEx(controller->device, &controller->hid.read_ovl); +- WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); +- SetEvent(update_event); +-} +- +-static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) +-{ +- EnterCriticalSection(&controller->crit); +- +- if (controller->device) +- { +- TRACE("removing device %s from index %Iu\n", debugstr_w(controller->device_path), controller - controllers); +- +- if (!already_removed) controller_disable(controller); +- CloseHandle(controller->device); +- controller->device = NULL; +- +- free(controller->hid.input_report_buf); +- free(controller->hid.output_report_buf); +- free(controller->hid.feature_report_buf); +- HidD_FreePreparsedData(controller->hid.preparsed); +- memset(&controller->hid, 0, sizeof(controller->hid)); +- } +- +- LeaveCriticalSection(&controller->crit); +-} ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed); + + static void controller_enable(struct xinput_controller *controller) + { +@@ -381,6 +359,19 @@ static void controller_enable(struct xinput_controller *controller) + else SetEvent(update_event); + } + ++static void controller_disable(struct xinput_controller *controller) ++{ ++ XINPUT_VIBRATION state = {0}; ++ ++ if (!controller->enabled) return; ++ if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); ++ controller->enabled = FALSE; ++ ++ CancelIoEx(controller->device, &controller->hid.read_ovl); ++ WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); ++ SetEvent(update_event); ++} ++ + static BOOL controller_init(struct xinput_controller *controller, PHIDP_PREPARSED_DATA preparsed, + HIDP_CAPS *caps, HANDLE device, const WCHAR *device_path) + { +@@ -464,17 +455,21 @@ static BOOL device_is_overridden(HANDLE device) + return disable; + } + +-static void open_device_at_index(const WCHAR *device_path, int index) ++static BOOL try_add_device(const WCHAR *device_path) + { + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + NTSTATUS status; + HANDLE device; ++ int i; ++ ++ if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ ++ if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ + + device = CreateFileW(device_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); +- if (device == INVALID_HANDLE_VALUE) return; ++ if (device == INVALID_HANDLE_VALUE) return TRUE; + + preparsed = NULL; + if (!HidD_GetPreparsedData(device, &preparsed)) +@@ -488,60 +483,13 @@ static void open_device_at_index(const WCHAR *device_path, int index) + WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps.UsagePage, caps.Usage); + else if (device_is_overridden(device)) + WARN("ignoring HID device, overridden for dinput\n"); +- else if (!controller_init(&controllers[index], preparsed, &caps, device, device_path)) ++ else if (!controller_init(&controllers[i], preparsed, &caps, device, device_path)) + WARN("ignoring HID device, failed to initialize\n"); + else +- { +- TRACE("opened device %s at index %u\n", debugstr_w(device_path), index); +- return; +- } ++ return TRUE; + + CloseHandle(device); + HidD_FreePreparsedData(preparsed); +-} +- +-static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) +-{ +- int i; +- +- *free_slot = XUSER_MAX_COUNT; +- for (i = XUSER_MAX_COUNT; i > 0; i--) +- { +- if (!controllers[i - 1].device) *free_slot = i - 1; +- else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; +- } +- +- /* CW-Bug-Id: #20528 Keep steam virtual controller ordered, swap existing controllers out of the slot */ +- if ((swscanf(device_path, L"\\\\?\\hid#vid_045e&pid_028e&xi_%02x#", &i) == 1 || +- swscanf(device_path, L"\\\\?\\HID#VID_045E&PID_028E&XI_%02X#", &i) == 1) && +- i > 0 && i <= XUSER_MAX_COUNT && *free_slot != i - 1) +- { +- controller_destroy(&controllers[i - 1], TRUE); +- if (*free_slot != XUSER_MAX_COUNT) open_device_at_index(controllers[i - 1].device_path, *free_slot); +- *free_slot = i - 1; +- } +- +- /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ +- if ((swscanf(device_path, L"\\\\?\\hid#vid_28de&pid_11ff&xi_%02u#", &i) == 1 || +- swscanf(device_path, L"\\\\?\\HID#VID_28DE&PID_11FF&XI_%02u#", &i) == 1) && +- i < XUSER_MAX_COUNT && *free_slot != i) +- { +- controller_destroy(&controllers[i], TRUE); +- if (*free_slot != XUSER_MAX_COUNT) open_device_at_index(controllers[i].device_path, *free_slot); +- *free_slot = i; +- } +- +- return FALSE; +-} +- +-static BOOL try_add_device(const WCHAR *device_path) +-{ +- SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; +- int i; +- +- if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ +- if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ +- open_device_at_index(device_path, i); + return TRUE; + } + +@@ -569,6 +527,26 @@ static void update_controller_list(void) + SetupDiDestroyDeviceInfoList(set); + } + ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) ++{ ++ EnterCriticalSection(&controller->crit); ++ ++ if (controller->device) ++ { ++ if (!already_removed) controller_disable(controller); ++ CloseHandle(controller->device); ++ controller->device = NULL; ++ ++ free(controller->hid.input_report_buf); ++ free(controller->hid.output_report_buf); ++ free(controller->hid.feature_report_buf); ++ HidD_FreePreparsedData(controller->hid.preparsed); ++ memset(&controller->hid, 0, sizeof(controller->hid)); ++ } ++ ++ LeaveCriticalSection(&controller->crit); ++} ++ + static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) + { + UINT sign = 1 << (caps->BitSize - 1); +@@ -779,9 +755,6 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void + { + HANDLE thread; + +- steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + start_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!start_event) ERR("failed to create start event, error %lu\n", GetLastError()); + +@@ -878,9 +851,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vib + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else ret = HID_set_state(&controllers[index], vibration); ++ ret = HID_set_state(&controllers[index], vibration); + + controller_unlock(&controllers[index]); + +@@ -898,10 +869,7 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state) + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else *state = controllers[index].state; +- ++ *state = controllers[index].state; + controller_unlock(&controllers[index]); + + return ERROR_SUCCESS; +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index c61e81ac645..9fd647eda0a 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -335,7 +335,6 @@ HKCR,ftp\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,http\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,https\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,mailto\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" +-HKCR,steam\shell\open\command,,,"""%16426%\Steam\Steam.exe"" -- ""%1""" + + [ContentIndex] + HKLM,System\CurrentControlSet\Control\ContentIndex\Language\Neutral,"WBreakerClass",,"{369647e0-17b0-11ce-9950-00aa004bbb1f}" +diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h +index 570ed52143f..0fed24fd7e7 100644 +--- a/programs/winedbg/debugger.h ++++ b/programs/winedbg/debugger.h +@@ -312,7 +312,6 @@ extern DWORD dbg_curr_tid; + extern dbg_ctx_t dbg_context; + extern BOOL dbg_interactiveP; + extern HANDLE dbg_houtput; +-extern HANDLE dbg_crash_report_file; + extern BOOL dbg_use_wine_dbg_output; + + struct dbg_internal_var +diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c +index 069d0a39c23..6d40506c671 100644 +--- a/programs/winedbg/tgt_active.c ++++ b/programs/winedbg/tgt_active.c +@@ -22,8 +22,6 @@ + #include + #include + #include +-#include +-#include + + #include "debugger.h" + #include "psapi.h" +@@ -798,48 +796,6 @@ static HANDLE create_temp_file(void) + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0 ); + } + +-static HANDLE create_crash_report_file(void) +-{ +- const char *dir = getenv("WINE_CRASH_REPORT_DIR"); +- const char *sgi; +- char timestr[32]; +- char name[MAX_PATH], *c; +- time_t t; +- struct tm lt; +- +- if(!dir || dir[0] == 0) +- return INVALID_HANDLE_VALUE; +- +- strcpy(name, dir); +- +- for(c = name + 1; *c; ++c){ +- if(*c == '/'){ +- *c = 0; +- CreateDirectoryA(name, NULL); +- *c = '/'; +- } +- } +- CreateDirectoryA(name, NULL); +- +- sgi = getenv("SteamGameId"); +- +- t = time(NULL); +- lt = *localtime(&t); +- strftime(timestr, ARRAY_SIZE(timestr), "%Y-%m-%d_%H:%M:%S", <); +- +- /* /path/to/crash/reports/2021-05-18_13:21:15_appid-976310_crash.log */ +- snprintf(name, ARRAY_SIZE(name), +- "%s%s/%s_appid-%s_crash.log", +- dir[0] == '/' ? "Z:/" : "", +- dir, +- timestr, +- sgi ? sgi : "0" +- ); +- +- return CreateFileA( name, GENERIC_WRITE, FILE_SHARE_READ, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); +-} +- + static const struct + { + int type; +@@ -1023,7 +979,6 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) + break; + case TRUE: + dbg_use_wine_dbg_output = TRUE; +- dbg_crash_report_file = create_crash_report_file(); + break; + } + +diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c +index 8cccf30fa09..048e6c2c2a7 100644 +--- a/programs/winedbg/winedbg.c ++++ b/programs/winedbg/winedbg.c +@@ -82,7 +82,6 @@ DWORD dbg_curr_pid = 0; + dbg_ctx_t dbg_context; + BOOL dbg_interactiveP = FALSE; + HANDLE dbg_houtput = 0; +-HANDLE dbg_crash_report_file = INVALID_HANDLE_VALUE; + BOOL dbg_use_wine_dbg_output = FALSE; + + static struct list dbg_process_list = LIST_INIT(dbg_process_list); +@@ -97,7 +96,10 @@ static void dbg_outputA(const char* buffer, int len) + DWORD w, i; + + if (dbg_use_wine_dbg_output) ++ { + __wine_dbg_output(buffer); ++ return; ++ } + + while (len > 0) + { +@@ -112,10 +114,7 @@ static void dbg_outputA(const char* buffer, int len) + if (len > 0) i = line_pos; /* buffer is full, flush anyway */ + else break; + } +- if (!dbg_use_wine_dbg_output) +- WriteFile(dbg_houtput, line_buff, i, &w, NULL); +- if (dbg_crash_report_file != INVALID_HANDLE_VALUE) +- WriteFile(dbg_crash_report_file, line_buff, i, &w, NULL); ++ WriteFile(dbg_houtput, line_buff, i, &w, NULL); + memmove( line_buff, line_buff + i, line_pos - i ); + line_pos -= i; + } +-- +2.40.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-exp.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-exp.mypatch new file mode 100644 index 000000000..5f89f81b7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80-exp.mypatch @@ -0,0 +1,774 @@ +From e016bbba7d61acee00402a8f16761f8e61fc165d Mon Sep 17 00:00:00 2001 +From: kotarac +Date: Wed, 24 May 2023 08:35:13 +0200 +Subject: [PATCH] de-steamify + +--- + dlls/advapi32/advapi.c | 34 ++++---- + dlls/dbghelp/dwarf.c | 5 -- + dlls/hidclass.sys/device.c | 7 +- + dlls/hidclass.sys/hid.h | 6 -- + dlls/hidclass.sys/pnp.c | 12 --- + dlls/kernelbase/process.c | 15 ---- + dlls/ntdll/loader.c | 2 +- + dlls/ntdll/unix/loader.c | 59 +------------- + dlls/secur32/secur32.c | 15 +--- + dlls/winebus.sys/bus_sdl.c | 6 +- + dlls/winex11.drv/window.c | 15 +--- + dlls/xinput1_3/main.c | 146 +++++++++++++--------------------- + loader/wine.inf.in | 1 - + programs/winedbg/debugger.h | 1 - + programs/winedbg/tgt_active.c | 45 ----------- + programs/winedbg/winedbg.c | 9 +-- + 16 files changed, 87 insertions(+), 291 deletions(-) + +diff --git a/dlls/advapi32/advapi.c b/dlls/advapi32/advapi.c +index a22e896c88a..6b3ffe2ea25 100644 +--- a/dlls/advapi32/advapi.c ++++ b/dlls/advapi32/advapi.c +@@ -44,15 +44,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(advapi); + */ + BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + { +- static const char steamuserA[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserA)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserA); +- return FALSE; +- } +- memcpy(name, steamuserA, sizeof(steamuserA)); +- *size = ARRAY_SIZE(steamuserA); +- return TRUE; ++ DWORD len = GetEnvironmentVariableA( "WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +@@ -60,15 +59,14 @@ BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + */ + BOOL WINAPI GetUserNameW( LPWSTR name, LPDWORD size ) + { +- static const WCHAR steamuserW[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserW)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserW); +- return FALSE; +- } +- memcpy(name, steamuserW, sizeof(steamuserW)); +- *size = ARRAY_SIZE(steamuserW); +- return TRUE; ++ DWORD len = GetEnvironmentVariableW( L"WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c +index a2d57173587..9ed63463513 100644 +--- a/dlls/dbghelp/dwarf.c ++++ b/dlls/dbghelp/dwarf.c +@@ -4200,11 +4200,6 @@ BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, + struct module_format* dwarf2_modfmt; + dwarf2_parse_module_context_t module_ctx; + +-/* Our DWARF parser has been known to crash winedbg in some cases. Since +- * probably no concerned parties are going to be using plain winedbg, just don't +- * bother parsing anything. */ +-return FALSE; +- + if (!dwarf2_init_section(&eh_frame, fmap, ".eh_frame", NULL, &eh_frame_sect)) + /* lld produces .eh_fram to avoid generating a long name */ + dwarf2_init_section(&eh_frame, fmap, ".eh_fram", NULL, &eh_frame_sect); +diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c +index 2092054d8ca..ac201afeddf 100644 +--- a/dlls/hidclass.sys/device.c ++++ b/dlls/hidclass.sys/device.c +@@ -223,7 +223,6 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + const BOOL polled = ext->u.pdo.information.Polled; + ULONG size, report_len = polled ? packet->reportBufferLen : desc->InputLength; + struct hid_report *last_report, *report; +- BOOL steam_overlay_open = FALSE; + struct hid_queue *queue; + LIST_ENTRY completed, *entry; + RAWINPUT *rawinput; +@@ -232,11 +231,7 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + + TRACE("device %p, packet %p\n", device, packet); + +- if (WaitForSingleObject(ext->steam_overlay_event, 0) == WAIT_OBJECT_0 || /* steam overlay is open */ +- WaitForSingleObject(ext->steam_keyboard_event, 0) == WAIT_OBJECT_0) /* steam keyboard is open */ +- steam_overlay_open = TRUE; +- +- if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID ) && !steam_overlay_open) ++ if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID )) + { + size = offsetof( RAWINPUT, data.hid.bRawData[report_len] ); + if (!(rawinput = malloc( size ))) ERR( "Failed to allocate rawinput data!\n" ); +diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h +index c14f8d7a942..b8cec55fb7c 100644 +--- a/dlls/hidclass.sys/hid.h ++++ b/dlls/hidclass.sys/hid.h +@@ -84,9 +84,6 @@ typedef struct _BASE_DEVICE_EXTENSION + WCHAR container_id[MAX_GUID_STRING_LEN]; + const GUID *class_guid; + +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; +- + BOOL is_fdo; + } BASE_DEVICE_EXTENSION; + +@@ -118,9 +115,6 @@ typedef struct _minidriver + + PDRIVER_ADD_DEVICE AddDevice; + PDRIVER_DISPATCH PNPDispatch; +- +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; + } minidriver; + + void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in_size, +diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c +index ca367ec3a7c..c0e2a874788 100644 +--- a/dlls/hidclass.sys/pnp.c ++++ b/dlls/hidclass.sys/pnp.c +@@ -178,9 +178,6 @@ static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *b + if (get_device_id(bus_pdo, BusQueryContainerID, ext->container_id)) + ext->container_id[0] = 0; + +- ext->steam_overlay_event = minidriver->steam_overlay_event; +- ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + is_xinput_class = !wcsncmp(device_id, L"WINEXINPUT\\", 7) && wcsstr(device_id, L"&XI_") != NULL; + if (is_xinput_class) ext->class_guid = &GUID_DEVINTERFACE_WINEXINPUT; + else ext->class_guid = &GUID_DEVINTERFACE_HID; +@@ -245,9 +242,6 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) + pdo_ext->u.pdo.information.VersionNumber = attr.VersionNumber; + pdo_ext->u.pdo.information.Polled = minidriver->minidriver.DevicesArePolled; + +- pdo_ext->steam_overlay_event = minidriver->steam_overlay_event; +- pdo_ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + call_minidriver( IOCTL_HID_GET_DEVICE_DESCRIPTOR, fdo, NULL, 0, &descriptor, sizeof(descriptor), &io ); + if (io.Status != STATUS_SUCCESS) + { +@@ -597,9 +591,6 @@ static void WINAPI driver_unload(DRIVER_OBJECT *driver) + if (md->DriverUnload) + md->DriverUnload(md->minidriver.DriverObject); + list_remove(&md->entry); +- +- CloseHandle(md->steam_overlay_event); +- CloseHandle(md->steam_keyboard_event); + free(md); + } + } +@@ -615,9 +606,6 @@ NTSTATUS WINAPI HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION *registration) + if (!(driver = calloc(1, sizeof(*driver)))) + return STATUS_NO_MEMORY; + +- driver->steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- driver->steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + driver->DriverUnload = registration->DriverObject->DriverUnload; + registration->DriverObject->DriverUnload = driver_unload; + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 0c5f5498df4..c869a5c9aa9 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -1223,21 +1223,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH OpenProcess( DWORD access, BOOL inherit, DWORD i + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + +- /* PROTON HACK: +- * On Windows, the Steam client puts its process ID into the registry +- * at: +- * +- * [HKCU\Software\Valve\Steam\ActiveProcess] +- * PID=dword:00000008 +- * +- * Games get that pid from the registry and then query it with +- * OpenProcess to ensure Steam is running. Since we aren't running the +- * Windows Steam in Wine, instead we hack this magic number into the +- * registry and then substitute the game's process itself in its place +- * so it can query a valid process. +- */ +- if (id == 0xfffe) id = GetCurrentProcessId(); +- + cid.UniqueProcess = ULongToHandle(id); + cid.UniqueThread = 0; + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 9bd07a9bec7..a827e4e47c1 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -86,7 +86,7 @@ const WCHAR windows_dir[] = L"C:\\windows"; + const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index e0e5006f498..f5e8a211ee6 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -773,42 +773,11 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; +- const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; + if (pe_info->image_flags & IMAGE_FLAGS_ComPlusNativeReady) machine = native_machine; + +- /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ +- if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && +- argv[3] && !strcmp( argv[3], "/desktop" )) +- { +- static char const gorso[] = "gameoverlayrenderer.so"; +- static int gorso_len = sizeof(gorso) - 1; +- int len = strlen( ld_preload ); +- char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); +- +- if (!env) return STATUS_NO_MEMORY; +- strcpy( env, "LD_PRELOAD=" ); +- strcat( env, ld_preload ); +- +- tmp = env + 11; +- do +- { +- if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); +- if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) +- { +- if (*next) memmove( tmp, next + 1, strlen(next) ); +- else *tmp = 0; +- next = tmp; +- } +- else tmp = next + 1; +- } +- while (*next); +- +- putenv( env ); +- } +- + signal( SIGPIPE, SIG_DFL ); + + sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); +@@ -2140,22 +2109,8 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + +-static void steamclient_write_jump(void *src_addr, void *tgt_addr) +-{ +-#ifdef _WIN64 +- static const char mov[] = {0x48, 0xb8}; +-#else +- static const char mov[] = {0xb8}; +-#endif +- static const char jmp[] = {0xff, 0xe0}; +- memcpy(src_addr, mov, sizeof(mov)); +- memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); +- memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); +-} +- + static NTSTATUS steamclient_setup_trampolines( void *args ) + { +- static int noexec_cached = -1; + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; +@@ -2166,13 +2121,10 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name, *wsne; ++ char *name; + UINT_PTR page_mask; + int i; + +- if (noexec_cached == -1) +- noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); +- + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2181,8 +2133,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- if (noexec_cached) mprotect(addr, size, PROT_READ); +- else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); ++ mprotect(addr, size, PROT_READ); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2196,8 +2147,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2205,8 +2155,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + + return STATUS_SUCCESS; + } +diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c +index bf94b1a594e..d703757bae7 100644 +--- a/dlls/secur32/secur32.c ++++ b/dlls/secur32/secur32.c +@@ -1135,22 +1135,9 @@ BOOLEAN WINAPI GetUserNameExW( + return FALSE; + } + +- case NameDisplay: +- { +- static const WCHAR wineusernameW[] = {'W','I','N','E','U','S','E','R','N','A','M','E',0}; +- +- DWORD needed = GetEnvironmentVariableW(wineusernameW, NULL, 0); +- if (*nSize < needed) { +- *nSize = needed; +- SetLastError(ERROR_MORE_DATA); +- return FALSE; +- } +- *nSize = GetEnvironmentVariableW(wineusernameW, lpNameBuffer, *nSize); +- return TRUE; +- } +- + case NameUnknown: + case NameFullyQualifiedDN: ++ case NameDisplay: + case NameUniqueId: + case NameCanonical: + case NameUserPrincipal: +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index f4414ae1f50..4ed55ce9c01 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -965,7 +965,7 @@ static void sdl_add_device(unsigned int index) + SDL_JoystickID id; + SDL_JoystickType joystick_type; + SDL_GameController *controller = NULL; +- const char *product, *sdl_serial, *str; ++ const char *product, *sdl_serial; + char guid_str[33], buffer[ARRAY_SIZE(desc.product)]; + int axis_count, axis_offset; + +@@ -1021,10 +1021,6 @@ static void sdl_add_device(unsigned int index) + desc.pid = 0x028e; + } + +- /* CW-Bug-Id: #20528 Check steam virtual controller indexes to keep them ordered */ +- if ((str = pSDL_JoystickName(joystick)) && sscanf(str, "Microsoft X-Box 360 pad %u", &desc.input) == 1) desc.input++; +- else desc.input = -1; +- + if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) + { + ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 116342f45c0..64bdf34552d 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1055,19 +1055,8 @@ static void set_initial_wm_hints( Display *display, Window window ) + /* class hints */ + if ((class_hints = XAllocClassHint())) + { +- static char steam_proton[] = "steam_proton"; +- const char *app_id = getenv("SteamAppId"); +- char proton_app_class[128]; +- +- if(app_id && *app_id){ +- snprintf(proton_app_class, sizeof(proton_app_class), "steam_app_%s", app_id); +- class_hints->res_name = proton_app_class; +- class_hints->res_class = proton_app_class; +- }else{ +- class_hints->res_name = steam_proton; +- class_hints->res_class = steam_proton; +- } +- ++ class_hints->res_name = process_name; ++ class_hints->res_class = process_name; + XSetClassHint( display, window, class_hints ); + XFree( class_hints ); + } +diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c +index 192d75413fa..2abf33a8d45 100644 +--- a/dlls/xinput1_3/main.c ++++ b/dlls/xinput1_3/main.c +@@ -124,8 +124,19 @@ static HANDLE start_event; + static HANDLE stop_event; + static HANDLE done_event; + static HANDLE update_event; +-static HANDLE steam_overlay_event; +-static HANDLE steam_keyboard_event; ++ ++static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) ++{ ++ int i; ++ ++ *free_slot = XUSER_MAX_COUNT; ++ for (i = XUSER_MAX_COUNT; i > 0; i--) ++ { ++ if (!controllers[i - 1].device) *free_slot = i - 1; ++ else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; ++ } ++ return FALSE; ++} + + static void check_value_caps(struct xinput_controller *controller, USHORT usage, HIDP_VALUE_CAPS *caps) + { +@@ -328,40 +339,7 @@ static DWORD HID_set_state(struct xinput_controller *controller, XINPUT_VIBRATIO + return ERROR_SUCCESS; + } + +-static void controller_disable(struct xinput_controller *controller) +-{ +- XINPUT_VIBRATION state = {0}; +- +- if (!controller->enabled) return; +- if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); +- controller->enabled = FALSE; +- +- CancelIoEx(controller->device, &controller->hid.read_ovl); +- WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); +- SetEvent(update_event); +-} +- +-static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) +-{ +- EnterCriticalSection(&controller->crit); +- +- if (controller->device) +- { +- TRACE("removing device %s from index %Iu\n", debugstr_w(controller->device_path), controller - controllers); +- +- if (!already_removed) controller_disable(controller); +- CloseHandle(controller->device); +- controller->device = NULL; +- +- free(controller->hid.input_report_buf); +- free(controller->hid.output_report_buf); +- free(controller->hid.feature_report_buf); +- HidD_FreePreparsedData(controller->hid.preparsed); +- memset(&controller->hid, 0, sizeof(controller->hid)); +- } +- +- LeaveCriticalSection(&controller->crit); +-} ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed); + + static void controller_enable(struct xinput_controller *controller) + { +@@ -381,6 +359,19 @@ static void controller_enable(struct xinput_controller *controller) + else SetEvent(update_event); + } + ++static void controller_disable(struct xinput_controller *controller) ++{ ++ XINPUT_VIBRATION state = {0}; ++ ++ if (!controller->enabled) return; ++ if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); ++ controller->enabled = FALSE; ++ ++ CancelIoEx(controller->device, &controller->hid.read_ovl); ++ WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); ++ SetEvent(update_event); ++} ++ + static BOOL controller_init(struct xinput_controller *controller, PHIDP_PREPARSED_DATA preparsed, + HIDP_CAPS *caps, HANDLE device, const WCHAR *device_path) + { +@@ -464,17 +455,21 @@ static BOOL device_is_overridden(HANDLE device) + return disable; + } + +-static void open_device_at_index(const WCHAR *device_path, int index) ++static BOOL try_add_device(const WCHAR *device_path) + { + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + NTSTATUS status; + HANDLE device; ++ int i; ++ ++ if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ ++ if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ + + device = CreateFileW(device_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); +- if (device == INVALID_HANDLE_VALUE) return; ++ if (device == INVALID_HANDLE_VALUE) return TRUE; + + preparsed = NULL; + if (!HidD_GetPreparsedData(device, &preparsed)) +@@ -488,50 +483,13 @@ static void open_device_at_index(const WCHAR *device_path, int index) + WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps.UsagePage, caps.Usage); + else if (device_is_overridden(device)) + WARN("ignoring HID device, overridden for dinput\n"); +- else if (!controller_init(&controllers[index], preparsed, &caps, device, device_path)) ++ else if (!controller_init(&controllers[i], preparsed, &caps, device, device_path)) + WARN("ignoring HID device, failed to initialize\n"); + else +- { +- TRACE("opened device %s at index %u\n", debugstr_w(device_path), index); +- return; +- } ++ return TRUE; + + CloseHandle(device); + HidD_FreePreparsedData(preparsed); +-} +- +-static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) +-{ +- int i; +- +- *free_slot = XUSER_MAX_COUNT; +- for (i = XUSER_MAX_COUNT; i > 0; i--) +- { +- if (!controllers[i - 1].device) *free_slot = i - 1; +- else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; +- } +- +- /* CW-Bug-Id: #20528 Keep steam virtual controller ordered, swap existing controllers out of the slot */ +- if ((swscanf(device_path, L"\\\\?\\hid#vid_045e&pid_028e&xi_%02x#", &i) == 1 || +- swscanf(device_path, L"\\\\?\\HID#VID_045E&PID_028E&XI_%02X#", &i) == 1) && +- i > 0 && i <= XUSER_MAX_COUNT && *free_slot != i - 1) +- { +- controller_destroy(&controllers[i - 1], TRUE); +- if (*free_slot != XUSER_MAX_COUNT) open_device_at_index(controllers[i - 1].device_path, *free_slot); +- *free_slot = i - 1; +- } +- +- return FALSE; +-} +- +-static BOOL try_add_device(const WCHAR *device_path) +-{ +- SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; +- int i; +- +- if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ +- if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ +- open_device_at_index(device_path, i); + return TRUE; + } + +@@ -569,6 +527,26 @@ static void update_controller_list(void) + SetupDiDestroyDeviceInfoList(set); + } + ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) ++{ ++ EnterCriticalSection(&controller->crit); ++ ++ if (controller->device) ++ { ++ if (!already_removed) controller_disable(controller); ++ CloseHandle(controller->device); ++ controller->device = NULL; ++ ++ free(controller->hid.input_report_buf); ++ free(controller->hid.output_report_buf); ++ free(controller->hid.feature_report_buf); ++ HidD_FreePreparsedData(controller->hid.preparsed); ++ memset(&controller->hid, 0, sizeof(controller->hid)); ++ } ++ ++ LeaveCriticalSection(&controller->crit); ++} ++ + static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) + { + UINT sign = 1 << (caps->BitSize - 1); +@@ -779,9 +755,6 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void + { + HANDLE thread; + +- steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + start_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!start_event) ERR("failed to create start event, error %lu\n", GetLastError()); + +@@ -878,9 +851,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vib + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else ret = HID_set_state(&controllers[index], vibration); ++ ret = HID_set_state(&controllers[index], vibration); + + controller_unlock(&controllers[index]); + +@@ -898,10 +869,7 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state) + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else *state = controllers[index].state; +- ++ *state = controllers[index].state; + controller_unlock(&controllers[index]); + + return ERROR_SUCCESS; +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index c61e81ac645..9fd647eda0a 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -335,7 +335,6 @@ HKCR,ftp\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,http\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,https\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,mailto\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" +-HKCR,steam\shell\open\command,,,"""%16426%\Steam\Steam.exe"" -- ""%1""" + + [ContentIndex] + HKLM,System\CurrentControlSet\Control\ContentIndex\Language\Neutral,"WBreakerClass",,"{369647e0-17b0-11ce-9950-00aa004bbb1f}" +diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h +index 570ed52143f..0fed24fd7e7 100644 +--- a/programs/winedbg/debugger.h ++++ b/programs/winedbg/debugger.h +@@ -312,7 +312,6 @@ extern DWORD dbg_curr_tid; + extern dbg_ctx_t dbg_context; + extern BOOL dbg_interactiveP; + extern HANDLE dbg_houtput; +-extern HANDLE dbg_crash_report_file; + extern BOOL dbg_use_wine_dbg_output; + + struct dbg_internal_var +diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c +index 069d0a39c23..6d40506c671 100644 +--- a/programs/winedbg/tgt_active.c ++++ b/programs/winedbg/tgt_active.c +@@ -22,8 +22,6 @@ + #include + #include + #include +-#include +-#include + + #include "debugger.h" + #include "psapi.h" +@@ -798,48 +796,6 @@ static HANDLE create_temp_file(void) + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0 ); + } + +-static HANDLE create_crash_report_file(void) +-{ +- const char *dir = getenv("WINE_CRASH_REPORT_DIR"); +- const char *sgi; +- char timestr[32]; +- char name[MAX_PATH], *c; +- time_t t; +- struct tm lt; +- +- if(!dir || dir[0] == 0) +- return INVALID_HANDLE_VALUE; +- +- strcpy(name, dir); +- +- for(c = name + 1; *c; ++c){ +- if(*c == '/'){ +- *c = 0; +- CreateDirectoryA(name, NULL); +- *c = '/'; +- } +- } +- CreateDirectoryA(name, NULL); +- +- sgi = getenv("SteamGameId"); +- +- t = time(NULL); +- lt = *localtime(&t); +- strftime(timestr, ARRAY_SIZE(timestr), "%Y-%m-%d_%H:%M:%S", <); +- +- /* /path/to/crash/reports/2021-05-18_13:21:15_appid-976310_crash.log */ +- snprintf(name, ARRAY_SIZE(name), +- "%s%s/%s_appid-%s_crash.log", +- dir[0] == '/' ? "Z:/" : "", +- dir, +- timestr, +- sgi ? sgi : "0" +- ); +- +- return CreateFileA( name, GENERIC_WRITE, FILE_SHARE_READ, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); +-} +- + static const struct + { + int type; +@@ -1023,7 +979,6 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) + break; + case TRUE: + dbg_use_wine_dbg_output = TRUE; +- dbg_crash_report_file = create_crash_report_file(); + break; + } + +diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c +index 8cccf30fa09..048e6c2c2a7 100644 +--- a/programs/winedbg/winedbg.c ++++ b/programs/winedbg/winedbg.c +@@ -82,7 +82,6 @@ DWORD dbg_curr_pid = 0; + dbg_ctx_t dbg_context; + BOOL dbg_interactiveP = FALSE; + HANDLE dbg_houtput = 0; +-HANDLE dbg_crash_report_file = INVALID_HANDLE_VALUE; + BOOL dbg_use_wine_dbg_output = FALSE; + + static struct list dbg_process_list = LIST_INIT(dbg_process_list); +@@ -97,7 +96,10 @@ static void dbg_outputA(const char* buffer, int len) + DWORD w, i; + + if (dbg_use_wine_dbg_output) ++ { + __wine_dbg_output(buffer); ++ return; ++ } + + while (len > 0) + { +@@ -112,10 +114,7 @@ static void dbg_outputA(const char* buffer, int len) + if (len > 0) i = line_pos; /* buffer is full, flush anyway */ + else break; + } +- if (!dbg_use_wine_dbg_output) +- WriteFile(dbg_houtput, line_buff, i, &w, NULL); +- if (dbg_crash_report_file != INVALID_HANDLE_VALUE) +- WriteFile(dbg_crash_report_file, line_buff, i, &w, NULL); ++ WriteFile(dbg_houtput, line_buff, i, &w, NULL); + memmove( line_buff, line_buff + i, line_pos - i ); + line_pos -= i; + } +-- +2.40.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80.mypatch new file mode 100644 index 000000000..6bf7c72cf --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/de-steamify-80.mypatch @@ -0,0 +1,774 @@ +From c9e5a91b9aa3b21e1b007b6177deb669b4e9b349 Mon Sep 17 00:00:00 2001 +From: kotarac +Date: Wed, 24 May 2023 08:34:17 +0200 +Subject: [PATCH] de-steamify + +--- + dlls/advapi32/advapi.c | 34 ++++---- + dlls/dbghelp/dwarf.c | 5 -- + dlls/hidclass.sys/device.c | 7 +- + dlls/hidclass.sys/hid.h | 6 -- + dlls/hidclass.sys/pnp.c | 12 --- + dlls/kernelbase/process.c | 15 ---- + dlls/ntdll/loader.c | 2 +- + dlls/ntdll/unix/loader.c | 59 +------------- + dlls/secur32/secur32.c | 15 +--- + dlls/winebus.sys/bus_sdl.c | 6 +- + dlls/winex11.drv/window.c | 15 +--- + dlls/xinput1_3/main.c | 146 +++++++++++++--------------------- + loader/wine.inf.in | 1 - + programs/winedbg/debugger.h | 1 - + programs/winedbg/tgt_active.c | 45 ----------- + programs/winedbg/winedbg.c | 9 +-- + 16 files changed, 87 insertions(+), 291 deletions(-) + +diff --git a/dlls/advapi32/advapi.c b/dlls/advapi32/advapi.c +index a22e896c88a..6b3ffe2ea25 100644 +--- a/dlls/advapi32/advapi.c ++++ b/dlls/advapi32/advapi.c +@@ -44,15 +44,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(advapi); + */ + BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + { +- static const char steamuserA[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserA)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserA); +- return FALSE; +- } +- memcpy(name, steamuserA, sizeof(steamuserA)); +- *size = ARRAY_SIZE(steamuserA); +- return TRUE; ++ DWORD len = GetEnvironmentVariableA( "WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +@@ -60,15 +59,14 @@ BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + */ + BOOL WINAPI GetUserNameW( LPWSTR name, LPDWORD size ) + { +- static const WCHAR steamuserW[] = {'s','t','e','a','m','u','s','e','r',0}; +- if(*size < ARRAY_SIZE(steamuserW)){ +- SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = ARRAY_SIZE(steamuserW); +- return FALSE; +- } +- memcpy(name, steamuserW, sizeof(steamuserW)); +- *size = ARRAY_SIZE(steamuserW); +- return TRUE; ++ DWORD len = GetEnvironmentVariableW( L"WINEUSERNAME", name, *size ); ++ BOOL ret; ++ ++ if (!len) return FALSE; ++ if ((ret = (len < *size))) len++; ++ else SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = len; ++ return ret; + } + + /****************************************************************************** +diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c +index a2d57173587..9ed63463513 100644 +--- a/dlls/dbghelp/dwarf.c ++++ b/dlls/dbghelp/dwarf.c +@@ -4200,11 +4200,6 @@ BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, + struct module_format* dwarf2_modfmt; + dwarf2_parse_module_context_t module_ctx; + +-/* Our DWARF parser has been known to crash winedbg in some cases. Since +- * probably no concerned parties are going to be using plain winedbg, just don't +- * bother parsing anything. */ +-return FALSE; +- + if (!dwarf2_init_section(&eh_frame, fmap, ".eh_frame", NULL, &eh_frame_sect)) + /* lld produces .eh_fram to avoid generating a long name */ + dwarf2_init_section(&eh_frame, fmap, ".eh_fram", NULL, &eh_frame_sect); +diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c +index 2092054d8ca..ac201afeddf 100644 +--- a/dlls/hidclass.sys/device.c ++++ b/dlls/hidclass.sys/device.c +@@ -223,7 +223,6 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + const BOOL polled = ext->u.pdo.information.Polled; + ULONG size, report_len = polled ? packet->reportBufferLen : desc->InputLength; + struct hid_report *last_report, *report; +- BOOL steam_overlay_open = FALSE; + struct hid_queue *queue; + LIST_ENTRY completed, *entry; + RAWINPUT *rawinput; +@@ -232,11 +231,7 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack + + TRACE("device %p, packet %p\n", device, packet); + +- if (WaitForSingleObject(ext->steam_overlay_event, 0) == WAIT_OBJECT_0 || /* steam overlay is open */ +- WaitForSingleObject(ext->steam_keyboard_event, 0) == WAIT_OBJECT_0) /* steam keyboard is open */ +- steam_overlay_open = TRUE; +- +- if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID ) && !steam_overlay_open) ++ if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID )) + { + size = offsetof( RAWINPUT, data.hid.bRawData[report_len] ); + if (!(rawinput = malloc( size ))) ERR( "Failed to allocate rawinput data!\n" ); +diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h +index c14f8d7a942..b8cec55fb7c 100644 +--- a/dlls/hidclass.sys/hid.h ++++ b/dlls/hidclass.sys/hid.h +@@ -84,9 +84,6 @@ typedef struct _BASE_DEVICE_EXTENSION + WCHAR container_id[MAX_GUID_STRING_LEN]; + const GUID *class_guid; + +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; +- + BOOL is_fdo; + } BASE_DEVICE_EXTENSION; + +@@ -118,9 +115,6 @@ typedef struct _minidriver + + PDRIVER_ADD_DEVICE AddDevice; + PDRIVER_DISPATCH PNPDispatch; +- +- HANDLE steam_overlay_event; +- HANDLE steam_keyboard_event; + } minidriver; + + void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in_size, +diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c +index ca367ec3a7c..c0e2a874788 100644 +--- a/dlls/hidclass.sys/pnp.c ++++ b/dlls/hidclass.sys/pnp.c +@@ -178,9 +178,6 @@ static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *b + if (get_device_id(bus_pdo, BusQueryContainerID, ext->container_id)) + ext->container_id[0] = 0; + +- ext->steam_overlay_event = minidriver->steam_overlay_event; +- ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + is_xinput_class = !wcsncmp(device_id, L"WINEXINPUT\\", 7) && wcsstr(device_id, L"&XI_") != NULL; + if (is_xinput_class) ext->class_guid = &GUID_DEVINTERFACE_WINEXINPUT; + else ext->class_guid = &GUID_DEVINTERFACE_HID; +@@ -245,9 +242,6 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) + pdo_ext->u.pdo.information.VersionNumber = attr.VersionNumber; + pdo_ext->u.pdo.information.Polled = minidriver->minidriver.DevicesArePolled; + +- pdo_ext->steam_overlay_event = minidriver->steam_overlay_event; +- pdo_ext->steam_keyboard_event = minidriver->steam_keyboard_event; +- + call_minidriver( IOCTL_HID_GET_DEVICE_DESCRIPTOR, fdo, NULL, 0, &descriptor, sizeof(descriptor), &io ); + if (io.Status != STATUS_SUCCESS) + { +@@ -597,9 +591,6 @@ static void WINAPI driver_unload(DRIVER_OBJECT *driver) + if (md->DriverUnload) + md->DriverUnload(md->minidriver.DriverObject); + list_remove(&md->entry); +- +- CloseHandle(md->steam_overlay_event); +- CloseHandle(md->steam_keyboard_event); + free(md); + } + } +@@ -615,9 +606,6 @@ NTSTATUS WINAPI HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION *registration) + if (!(driver = calloc(1, sizeof(*driver)))) + return STATUS_NO_MEMORY; + +- driver->steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- driver->steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + driver->DriverUnload = registration->DriverObject->DriverUnload; + registration->DriverObject->DriverUnload = driver_unload; + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 2fe9d93e76e..2fa5a1da88e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -1232,21 +1232,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH OpenProcess( DWORD access, BOOL inherit, DWORD i + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + +- /* PROTON HACK: +- * On Windows, the Steam client puts its process ID into the registry +- * at: +- * +- * [HKCU\Software\Valve\Steam\ActiveProcess] +- * PID=dword:00000008 +- * +- * Games get that pid from the registry and then query it with +- * OpenProcess to ensure Steam is running. Since we aren't running the +- * Windows Steam in Wine, instead we hack this magic number into the +- * registry and then substitute the game's process itself in its place +- * so it can query a valid process. +- */ +- if (id == 0xfffe) id = GetCurrentProcessId(); +- + cid.UniqueProcess = ULongToHandle(id); + cid.UniqueThread = 0; + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 14fc568f0d2..6c068fd6afe 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -86,7 +86,7 @@ const WCHAR windows_dir[] = L"C:\\windows"; + const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ef38691b15a..f525b614211 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -773,42 +773,11 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; +- const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; + if (pe_info->image_flags & IMAGE_FLAGS_ComPlusNativeReady) machine = native_machine; + +- /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ +- if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && +- argv[3] && !strcmp( argv[3], "/desktop" )) +- { +- static char const gorso[] = "gameoverlayrenderer.so"; +- static int gorso_len = sizeof(gorso) - 1; +- int len = strlen( ld_preload ); +- char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); +- +- if (!env) return STATUS_NO_MEMORY; +- strcpy( env, "LD_PRELOAD=" ); +- strcat( env, ld_preload ); +- +- tmp = env + 11; +- do +- { +- if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); +- if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) +- { +- if (*next) memmove( tmp, next + 1, strlen(next) ); +- else *tmp = 0; +- next = tmp; +- } +- else tmp = next + 1; +- } +- while (*next); +- +- putenv( env ); +- } +- + signal( SIGPIPE, SIG_DFL ); + + sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); +@@ -2140,22 +2109,8 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + +-static void steamclient_write_jump(void *src_addr, void *tgt_addr) +-{ +-#ifdef _WIN64 +- static const char mov[] = {0x48, 0xb8}; +-#else +- static const char mov[] = {0xb8}; +-#endif +- static const char jmp[] = {0xff, 0xe0}; +- memcpy(src_addr, mov, sizeof(mov)); +- memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); +- memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); +-} +- + static NTSTATUS steamclient_setup_trampolines( void *args ) + { +- static int noexec_cached = -1; + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; +@@ -2166,13 +2121,10 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name, *wsne; ++ char *name; + UINT_PTR page_mask; + int i; + +- if (noexec_cached == -1) +- noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); +- + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2181,8 +2133,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- if (noexec_cached) mprotect(addr, size, PROT_READ); +- else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); ++ mprotect(addr, size, PROT_READ); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2196,8 +2147,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2205,8 +2155,7 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); +- else steamclient_count++; ++ steamclient_count++; + + return STATUS_SUCCESS; + } +diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c +index bf94b1a594e..d703757bae7 100644 +--- a/dlls/secur32/secur32.c ++++ b/dlls/secur32/secur32.c +@@ -1135,22 +1135,9 @@ BOOLEAN WINAPI GetUserNameExW( + return FALSE; + } + +- case NameDisplay: +- { +- static const WCHAR wineusernameW[] = {'W','I','N','E','U','S','E','R','N','A','M','E',0}; +- +- DWORD needed = GetEnvironmentVariableW(wineusernameW, NULL, 0); +- if (*nSize < needed) { +- *nSize = needed; +- SetLastError(ERROR_MORE_DATA); +- return FALSE; +- } +- *nSize = GetEnvironmentVariableW(wineusernameW, lpNameBuffer, *nSize); +- return TRUE; +- } +- + case NameUnknown: + case NameFullyQualifiedDN: ++ case NameDisplay: + case NameUniqueId: + case NameCanonical: + case NameUserPrincipal: +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index f4414ae1f50..4ed55ce9c01 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -965,7 +965,7 @@ static void sdl_add_device(unsigned int index) + SDL_JoystickID id; + SDL_JoystickType joystick_type; + SDL_GameController *controller = NULL; +- const char *product, *sdl_serial, *str; ++ const char *product, *sdl_serial; + char guid_str[33], buffer[ARRAY_SIZE(desc.product)]; + int axis_count, axis_offset; + +@@ -1021,10 +1021,6 @@ static void sdl_add_device(unsigned int index) + desc.pid = 0x028e; + } + +- /* CW-Bug-Id: #20528 Check steam virtual controller indexes to keep them ordered */ +- if ((str = pSDL_JoystickName(joystick)) && sscanf(str, "Microsoft X-Box 360 pad %u", &desc.input) == 1) desc.input++; +- else desc.input = -1; +- + if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) + { + ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 697203dd499..44b4261cf82 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1055,19 +1055,8 @@ static void set_initial_wm_hints( Display *display, Window window ) + /* class hints */ + if ((class_hints = XAllocClassHint())) + { +- static char steam_proton[] = "steam_proton"; +- const char *app_id = getenv("SteamAppId"); +- char proton_app_class[128]; +- +- if(app_id && *app_id){ +- snprintf(proton_app_class, sizeof(proton_app_class), "steam_app_%s", app_id); +- class_hints->res_name = proton_app_class; +- class_hints->res_class = proton_app_class; +- }else{ +- class_hints->res_name = steam_proton; +- class_hints->res_class = steam_proton; +- } +- ++ class_hints->res_name = process_name; ++ class_hints->res_class = process_name; + XSetClassHint( display, window, class_hints ); + XFree( class_hints ); + } +diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c +index 192d75413fa..2abf33a8d45 100644 +--- a/dlls/xinput1_3/main.c ++++ b/dlls/xinput1_3/main.c +@@ -124,8 +124,19 @@ static HANDLE start_event; + static HANDLE stop_event; + static HANDLE done_event; + static HANDLE update_event; +-static HANDLE steam_overlay_event; +-static HANDLE steam_keyboard_event; ++ ++static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) ++{ ++ int i; ++ ++ *free_slot = XUSER_MAX_COUNT; ++ for (i = XUSER_MAX_COUNT; i > 0; i--) ++ { ++ if (!controllers[i - 1].device) *free_slot = i - 1; ++ else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; ++ } ++ return FALSE; ++} + + static void check_value_caps(struct xinput_controller *controller, USHORT usage, HIDP_VALUE_CAPS *caps) + { +@@ -328,40 +339,7 @@ static DWORD HID_set_state(struct xinput_controller *controller, XINPUT_VIBRATIO + return ERROR_SUCCESS; + } + +-static void controller_disable(struct xinput_controller *controller) +-{ +- XINPUT_VIBRATION state = {0}; +- +- if (!controller->enabled) return; +- if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); +- controller->enabled = FALSE; +- +- CancelIoEx(controller->device, &controller->hid.read_ovl); +- WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); +- SetEvent(update_event); +-} +- +-static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) +-{ +- EnterCriticalSection(&controller->crit); +- +- if (controller->device) +- { +- TRACE("removing device %s from index %Iu\n", debugstr_w(controller->device_path), controller - controllers); +- +- if (!already_removed) controller_disable(controller); +- CloseHandle(controller->device); +- controller->device = NULL; +- +- free(controller->hid.input_report_buf); +- free(controller->hid.output_report_buf); +- free(controller->hid.feature_report_buf); +- HidD_FreePreparsedData(controller->hid.preparsed); +- memset(&controller->hid, 0, sizeof(controller->hid)); +- } +- +- LeaveCriticalSection(&controller->crit); +-} ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed); + + static void controller_enable(struct xinput_controller *controller) + { +@@ -381,6 +359,19 @@ static void controller_enable(struct xinput_controller *controller) + else SetEvent(update_event); + } + ++static void controller_disable(struct xinput_controller *controller) ++{ ++ XINPUT_VIBRATION state = {0}; ++ ++ if (!controller->enabled) return; ++ if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); ++ controller->enabled = FALSE; ++ ++ CancelIoEx(controller->device, &controller->hid.read_ovl); ++ WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); ++ SetEvent(update_event); ++} ++ + static BOOL controller_init(struct xinput_controller *controller, PHIDP_PREPARSED_DATA preparsed, + HIDP_CAPS *caps, HANDLE device, const WCHAR *device_path) + { +@@ -464,17 +455,21 @@ static BOOL device_is_overridden(HANDLE device) + return disable; + } + +-static void open_device_at_index(const WCHAR *device_path, int index) ++static BOOL try_add_device(const WCHAR *device_path) + { + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + NTSTATUS status; + HANDLE device; ++ int i; ++ ++ if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ ++ if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ + + device = CreateFileW(device_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); +- if (device == INVALID_HANDLE_VALUE) return; ++ if (device == INVALID_HANDLE_VALUE) return TRUE; + + preparsed = NULL; + if (!HidD_GetPreparsedData(device, &preparsed)) +@@ -488,50 +483,13 @@ static void open_device_at_index(const WCHAR *device_path, int index) + WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps.UsagePage, caps.Usage); + else if (device_is_overridden(device)) + WARN("ignoring HID device, overridden for dinput\n"); +- else if (!controller_init(&controllers[index], preparsed, &caps, device, device_path)) ++ else if (!controller_init(&controllers[i], preparsed, &caps, device, device_path)) + WARN("ignoring HID device, failed to initialize\n"); + else +- { +- TRACE("opened device %s at index %u\n", debugstr_w(device_path), index); +- return; +- } ++ return TRUE; + + CloseHandle(device); + HidD_FreePreparsedData(preparsed); +-} +- +-static BOOL find_opened_device(const WCHAR *device_path, int *free_slot) +-{ +- int i; +- +- *free_slot = XUSER_MAX_COUNT; +- for (i = XUSER_MAX_COUNT; i > 0; i--) +- { +- if (!controllers[i - 1].device) *free_slot = i - 1; +- else if (!wcsicmp(device_path, controllers[i - 1].device_path)) return TRUE; +- } +- +- /* CW-Bug-Id: #20528 Keep steam virtual controller ordered, swap existing controllers out of the slot */ +- if ((swscanf(device_path, L"\\\\?\\hid#vid_045e&pid_028e&xi_%02x#", &i) == 1 || +- swscanf(device_path, L"\\\\?\\HID#VID_045E&PID_028E&XI_%02X#", &i) == 1) && +- i > 0 && i <= XUSER_MAX_COUNT && *free_slot != i - 1) +- { +- controller_destroy(&controllers[i - 1], TRUE); +- if (*free_slot != XUSER_MAX_COUNT) open_device_at_index(controllers[i - 1].device_path, *free_slot); +- *free_slot = i - 1; +- } +- +- return FALSE; +-} +- +-static BOOL try_add_device(const WCHAR *device_path) +-{ +- SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; +- int i; +- +- if (find_opened_device(device_path, &i)) return TRUE; /* already opened */ +- if (i == XUSER_MAX_COUNT) return FALSE; /* no more slots */ +- open_device_at_index(device_path, i); + return TRUE; + } + +@@ -569,6 +527,26 @@ static void update_controller_list(void) + SetupDiDestroyDeviceInfoList(set); + } + ++static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) ++{ ++ EnterCriticalSection(&controller->crit); ++ ++ if (controller->device) ++ { ++ if (!already_removed) controller_disable(controller); ++ CloseHandle(controller->device); ++ controller->device = NULL; ++ ++ free(controller->hid.input_report_buf); ++ free(controller->hid.output_report_buf); ++ free(controller->hid.feature_report_buf); ++ HidD_FreePreparsedData(controller->hid.preparsed); ++ memset(&controller->hid, 0, sizeof(controller->hid)); ++ } ++ ++ LeaveCriticalSection(&controller->crit); ++} ++ + static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) + { + UINT sign = 1 << (caps->BitSize - 1); +@@ -779,9 +755,6 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void + { + HANDLE thread; + +- steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); +- steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); +- + start_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!start_event) ERR("failed to create start event, error %lu\n", GetLastError()); + +@@ -878,9 +851,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vib + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) ret = ERROR_SUCCESS; +- else ret = HID_set_state(&controllers[index], vibration); ++ ret = HID_set_state(&controllers[index], vibration); + + controller_unlock(&controllers[index]); + +@@ -898,10 +869,7 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state) + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + +- if (WaitForSingleObject(steam_overlay_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else if (WaitForSingleObject(steam_keyboard_event, 0) == WAIT_OBJECT_0) memset(state, 0, sizeof(*state)); +- else *state = controllers[index].state; +- ++ *state = controllers[index].state; + controller_unlock(&controllers[index]); + + return ERROR_SUCCESS; +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 40dcd9eb0d2..1263d322587 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -335,7 +335,6 @@ HKCR,ftp\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,http\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,https\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,mailto\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" +-HKCR,steam\shell\open\command,,,"""%16426%\Steam\Steam.exe"" -- ""%1""" + + [ContentIndex] + HKLM,System\CurrentControlSet\Control\ContentIndex\Language\Neutral,"WBreakerClass",,"{369647e0-17b0-11ce-9950-00aa004bbb1f}" +diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h +index 4d2b946162b..6368248cd08 100644 +--- a/programs/winedbg/debugger.h ++++ b/programs/winedbg/debugger.h +@@ -304,7 +304,6 @@ extern DWORD dbg_curr_tid; + extern dbg_ctx_t dbg_context; + extern BOOL dbg_interactiveP; + extern HANDLE dbg_houtput; +-extern HANDLE dbg_crash_report_file; + extern BOOL dbg_use_wine_dbg_output; + + struct dbg_internal_var +diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c +index 5d660ab81dd..3c9b465e702 100644 +--- a/programs/winedbg/tgt_active.c ++++ b/programs/winedbg/tgt_active.c +@@ -22,8 +22,6 @@ + #include + #include + #include +-#include +-#include + + #include "debugger.h" + #include "psapi.h" +@@ -799,48 +797,6 @@ static HANDLE create_temp_file(void) + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0 ); + } + +-static HANDLE create_crash_report_file(void) +-{ +- const char *dir = getenv("WINE_CRASH_REPORT_DIR"); +- const char *sgi; +- char timestr[32]; +- char name[MAX_PATH], *c; +- time_t t; +- struct tm lt; +- +- if(!dir || dir[0] == 0) +- return INVALID_HANDLE_VALUE; +- +- strcpy(name, dir); +- +- for(c = name + 1; *c; ++c){ +- if(*c == '/'){ +- *c = 0; +- CreateDirectoryA(name, NULL); +- *c = '/'; +- } +- } +- CreateDirectoryA(name, NULL); +- +- sgi = getenv("SteamGameId"); +- +- t = time(NULL); +- lt = *localtime(&t); +- strftime(timestr, ARRAY_SIZE(timestr), "%Y-%m-%d_%H:%M:%S", <); +- +- /* /path/to/crash/reports/2021-05-18_13:21:15_appid-976310_crash.log */ +- snprintf(name, ARRAY_SIZE(name), +- "%s%s/%s_appid-%s_crash.log", +- dir[0] == '/' ? "Z:/" : "", +- dir, +- timestr, +- sgi ? sgi : "0" +- ); +- +- return CreateFileA( name, GENERIC_WRITE, FILE_SHARE_READ, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); +-} +- + static const struct + { + int type; +@@ -1024,7 +980,6 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) + break; + case TRUE: + dbg_use_wine_dbg_output = TRUE; +- dbg_crash_report_file = create_crash_report_file(); + break; + } + +diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c +index b2a1706117a..f0f02438934 100644 +--- a/programs/winedbg/winedbg.c ++++ b/programs/winedbg/winedbg.c +@@ -82,7 +82,6 @@ DWORD dbg_curr_pid = 0; + dbg_ctx_t dbg_context; + BOOL dbg_interactiveP = FALSE; + HANDLE dbg_houtput = 0; +-HANDLE dbg_crash_report_file = INVALID_HANDLE_VALUE; + BOOL dbg_use_wine_dbg_output = FALSE; + + static struct list dbg_process_list = LIST_INIT(dbg_process_list); +@@ -97,7 +96,10 @@ static void dbg_outputA(const char* buffer, int len) + DWORD w, i; + + if (dbg_use_wine_dbg_output) ++ { + __wine_dbg_output(buffer); ++ return; ++ } + + while (len > 0) + { +@@ -112,10 +114,7 @@ static void dbg_outputA(const char* buffer, int len) + if (len > 0) i = line_pos; /* buffer is full, flush anyway */ + else break; + } +- if (!dbg_use_wine_dbg_output) +- WriteFile(dbg_houtput, line_buff, i, &w, NULL); +- if (dbg_crash_report_file != INVALID_HANDLE_VALUE) +- WriteFile(dbg_crash_report_file, line_buff, i, &w, NULL); ++ WriteFile(dbg_houtput, line_buff, i, &w, NULL); + memmove( line_buff, line_buff + i, line_pos - i ); + line_pos -= i; + } +-- +2.40.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes index 41802929a..0aadb5330 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes @@ -2,10 +2,20 @@ # When not building proton, get rid of steam-specific patches if [ "$_EXTERNAL_INSTALL" != "proton" ]; then - if [ "$_LOCAL_PRESET" = "valve-exp-bleeding" ]; then - cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify-be.mypatch "$_where"/ + if [[ "$_plain_version" != *_8.0 ]]; then + if [[ "$_LOCAL_PRESET" = valve-ex* ]]; then + cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify-be.mypatch "$_where"/ + else + cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify.mypatch "$_where"/ + fi else - cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify.mypatch "$_where"/ + if [ "$_LOCAL_PRESET" = "valve" ]; then + cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify-80.mypatch "$_where"/ + elif [ "$_LOCAL_PRESET" = "valve-exp" ]; then + cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify-80-exp.mypatch "$_where"/ + else + cp "$_where"/wine-tkg-patches/hotfixes/valve/de-steamify-80-be.mypatch "$_where"/ + fi fi fi @@ -18,7 +28,7 @@ if [ "$_steamclient_noswap" = "true" ]; then fi if [ "$_use_staging" = "true" ]; then - if [ "$_LOCAL_PRESET" = "valve-exp-bleeding" ]; then + if [ "$_LOCAL_PRESET" = "valve-exp-bleeding" ] && [[ "$_plain_version" != *_8.0 ]]; then ( cd "${srcdir}/$_stgsrcdir" && patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/fixes/cryptext-CryptExtOpenCER/Rebase_against_be5d60d949c470d8039649ee1f7488a59a1c31bc.patch >> "$_where"/prepare.log ) fi @@ -28,6 +38,90 @@ if [ "$_use_staging" = "true" ]; then # gallium nine compat fix _hotfix_mainlinereverts+=(96b82203f192eade6910f4ac2ecb188e27d22feb) + # Bring back configure files. Staging uses them to regenerate fresh ones + # https://github.com/ValveSoftware/wine/commit/e813ca5771658b00875924ab88d525322e50d39f + _hotfix_mainlinereverts+=(e813ca5771658b00875924ab88d525322e50d39f) + + if [[ "$_plain_version" = *_8.0 ]]; then + _staging_args+=(-W winex11-_NET_ACTIVE_WINDOW \ + -W user32-alttab-focus \ + -W winex11-WM_WINDOWPOSCHANGING \ + -W winex11-MWM_Decorations \ + -W ntdll-Syscall_Emulation \ + -W ntdll-Junction_Points \ + -W server-File_Permissions \ + -W server-Stored_ACLs \ + -W eventfd_synchronization \ + -W d3dx11_43-D3DX11CreateTextureFromMemory \ + -W dbghelp-Debug_Symbols \ + -W ddraw-Device_Caps \ + -W Pipelight \ + -W dinput-joy-mappings \ + -W server-PeekMessage \ + -W server-Realtime_Priority \ + -W server-Signal_Thread \ + -W loader-KeyboardLayouts \ + -W msxml3-FreeThreadedXMLHTTP60 \ + -W ntdll-ForceBottomUpAlloc \ + -W ntdll-WRITECOPY \ + -W ntdll-Builtin_Prot \ + -W ntdll-CriticalSection \ + -W ntdll-Exception \ + -W ntdll-Hide_Wine_Exports \ + -W ntdll-Serial_Port_Detection \ + -W server-default_integrity \ + -W user32-rawinput-mouse \ + -W user32-rawinput-mouse-experimental \ + -W user32-recursive-activation \ + -W windows.networking.connectivity-new-dll \ + -W wineboot-ProxySettings \ + -W winex11-UpdateLayeredWindow \ + -W winex11-Vulkan_support \ + -W winex11-wglShareLists \ + -W wintab32-improvements \ + -W xactengine3_7-PrepareWave \ + -W xactengine-initial \ + -W kernel32-CopyFileEx \ + -W shell32-Progress_Dialog \ + -W shell32-ACE_Viewer \ + -W fltmgr.sys-FltBuildDefaultSecurityDescriptor \ + -W inseng-Implementation \ + -W ntdll-RtlQueryPackageIdentity \ + -W packager-DllMain \ + -W winemenubuilder-Desktop_Icon_Path \ + -W wscript-support-d-u-switches \ + -W wininet-Cleanup \ + -W sapi-ISpObjectToken-CreateInstance \ + -W cryptext-CryptExtOpenCER \ + -W shell32-NewMenu_Interface \ + -W wintrust-WTHelperGetProvCertFromChain \ + -W user32-FlashWindowEx \ + -W wined3d-zero-inf-shaders \ + -W kernel32-Debugger \ + -W mfplat-streaming-support \ + -W ntdll-Placeholders \ + -W ntdll-NtDevicePath \ + -W ntdll-wine-frames \ + -W winemenubuilder-integration \ + -W winspool.drv-ClosePrinter \ + -W winmm-mciSendCommandA \ + -W winemenubuilder-Desktop_Icon_Path \ + -W winemenubuilder-integration \ + -W winex11-XEMBED \ + -W winex11-CandidateWindowPos \ + -W winex11-Window_Style \ + -W winex11-ime-check-thread-data \ + -W winex11.drv-Query_server_position \ + -W user32-Mouse_Message_Hwnd \ + -W wined3d-SWVP-shaders \ + -W wined3d-Indexed_Vertex_Blending \ + -W winepulse-PulseAudio_Support) + if [[ "$_LOCAL_PRESET" = valve-ex* ]]; then + _staging_args+=(-W shell32-registry-lookup-app \ + -W d3dx9_36-D3DXStubs \ + -W ntdll-ext4-case-folder) + fi + else _staging_args+=(-W winex11-_NET_ACTIVE_WINDOW \ -W winex11-WM_WINDOWPOSCHANGING \ -W winex11-MWM_Decorations \ @@ -93,66 +187,117 @@ if [ "$_use_staging" = "true" ]; then -W cryptext-CryptExtOpenCER \ -W wined3d-zero-inf-shaders \ -W winex11-XEMBED) + fi fi _proton_staging() { msg2 "Manual application of some staging patches..." echo -e "\nManually applying some staging patches..." >> "$_where"/prepare.log + + if [[ "$_plain_version" = *_8.0 ]]; then + _proton_target="80" + else + _proton_target="70" + fi + patch -RNp1 < "${srcdir}"/"$_stgsrcdir"/patches/Compiler_Warnings/0031-include-Check-element-type-in-CONTAINING_RECORD-and-.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-Notification/0001-xactengine3.7-Delay-Notication-for-WAVEBANKPREPARED.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-Notification/0002-xactengine3_7-Record-context-for-each-notications.patch >> "$_where"/prepare.log + if [[ "$_plain_version" != *_8.0 ]]; then + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-Notification/0001-xactengine3.7-Delay-Notication-for-WAVEBANKPREPARED.patch >> "$_where"/prepare.log + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-Notification/0002-xactengine3_7-Record-context-for-each-notications.patch >> "$_where"/prepare.log + fi + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-PrepareWave/0002-xactengine3_7-Implement-IXACT3Engine-PrepareStreamin.patch >> "$_where"/prepare.log patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/xactengine3_7-PrepareWave/0003-xactengine3_7-Implement-IXACT3Engine-PrepareInMemory.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-Exception/0002-ntdll-OutputDebugString-should-throw-the-exception-a.patch >> "$_where"/prepare.log + + if [[ "$_plain_version" = *_8.0 ]]; then + if [[ "$_LOCAL_PRESET" = valve-ex* ]]; then + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch >> "$_where"/prepare.log + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch >> "$_where"/prepare.log + fi + else + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch >> "$_where"/prepare.log + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch >> "$_where"/prepare.log + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-Exception/0002-ntdll-OutputDebugString-should-throw-the-exception-a.patch >> "$_where"/prepare.log + fi + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-Hide_Wine_Exports/0001-ntdll-Add-support-for-hiding-wine-version-informatio.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/secur32-InitializeSecurityContextW/0001-secur32-Input-Parameter-should-be-NULL-on-first-call.patch >> "$_where"/prepare.log - #patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/wineboot-ProxySettings/0001-wineboot-Initialize-proxy-settings-registry-key.patch >> "$_where"/prepare.log + + if [[ "$_plain_version" != *_8.0 ]]; then + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/secur32-InitializeSecurityContextW/0001-secur32-Input-Parameter-should-be-NULL-on-first-call.patch >> "$_where"/prepare.log + fi + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/winex11-UpdateLayeredWindow/0001-winex11-Fix-alpha-blending-in-X11DRV_UpdateLayeredWi.patch >> "$_where"/prepare.log - patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-ApiSetMap/0001-ntdll-Add-dummy-apiset-to-PEB.patch >> "$_where"/prepare.log + + if [[ "$_plain_version" != *_8.0 ]]; then + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-ApiSetMap/0001-ntdll-Add-dummy-apiset-to-PEB.patch >> "$_where"/prepare.log + fi + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/shell32-NewMenu_Interface/0001-shell32-Implement-NewMenu-with-new-folder-item.patch >> "$_where"/prepare.log patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/cryptext-CryptExtOpenCER/0001-cryptext-Implement-CryptExtOpenCER.patch >> "$_where"/prepare.log + if [[ "$_plain_version" = *_8.0 ]]; then + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/kernel32-Debugger/0001-kernel32-Always-start-debugger-on-WinSta0.patch + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/winemenubuilder-integration/0001-winemenubuilder-Blacklist-desktop-integration-for-ce.patch + #patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/winex11-XEMBED/0001-winex11-Enable-disable-windows-when-they-are-un-mapped.patch + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/winepulse-PulseAudio_Support/0001-winepulse.drv-Use-a-separate-mainloop-and-ctx-for-pu.patch + if [[ "$_LOCAL_PRESET" = valve-ex* ]]; then + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/shell32-registry-lookup-app/0001-shell32-Append-.exe-when-registry-lookup-fails-first.patch + patch -Np1 < "${srcdir}"/"$_stgsrcdir"/patches/ntdll-ext4-case-folder/0002-ntdll-server-Mark-drive_c-as-case-insensitive-when-c.patch >> "$_where"/prepare.log + fi + fi + # Cherry picked GE patches - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch - - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch - - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/packager-DllMain/0001-packager-Prefer-native-version.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch - patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch - - # Clear non-compatible mypatches - rm -f "$_where"/{0002-futex_waitv,Shell32-CreateDirectoryInDestinationInFileOp-Move-multiop}.mypatch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch + + if [[ "$_plain_version" != *_8.0 ]]; then + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch + fi + + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch + + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch + if [[ "$_plain_version" = *_8.0 ]] && [[ "$_LOCAL_PRESET" = valve-ex* ]]; then + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f-be.patch + else + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch + fi + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch + + if [[ "$_plain_version" != *_8.0 ]]; then + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch + fi + + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/packager-DllMain/0001-packager-Prefer-native-version.patch + patch -Np1 < "$_where"/wine-tkg-patches/hotfixes/valve/staging-hotfixes/"$_proton_target"/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch } + +if [[ "$_LOCAL_PRESET" = valve* ]]; then + # Clear non-compatible mypatches + rm -f "$_where"/{0002-futex_waitv,Shell32-CreateDirectoryInDestinationInFileOp-Move-multiop}.mypatch +fi diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr-70.mypatch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr.mypatch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr-70.mypatch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr-80.mypatch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr-80.mypatch new file mode 100644 index 000000000..1bbd30567 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/proton-fsr-80.mypatch @@ -0,0 +1,2096 @@ +From 87da6603b15e47faf8eed0a7ad0fb6f2199472f5 Mon Sep 17 00:00:00 2001 +From: Ph42oN +Date: Sun, 9 Jul 2023 21:16:34 +0300 +Subject: [PATCH] proton8-fshack_amd_fsr + +--- + dlls/winevulkan/Makefile.in | 2 +- + dlls/winevulkan/make_vulkan | 2 +- + dlls/winevulkan/vulkan.c | 1483 +++++++++++++++++++++++++++--- + dlls/winevulkan/vulkan_private.h | 23 +- + dlls/winex11.drv/fs.c | 154 +++- + dlls/winex11.drv/vulkan.c | 8 +- + dlls/winex11.drv/x11drv.h | 1 + + 7 files changed, 1546 insertions(+), 127 deletions(-) + +diff --git a/dlls/winevulkan/Makefile.in b/dlls/winevulkan/Makefile.in +index 732bf5493a9..42d3764f3bb 100644 +--- a/dlls/winevulkan/Makefile.in ++++ b/dlls/winevulkan/Makefile.in +@@ -2,7 +2,7 @@ MODULE = winevulkan.dll + UNIXLIB = winevulkan.so + IMPORTLIB = winevulkan + IMPORTS = user32 gdi32 advapi32 setupapi win32u +-UNIX_LIBS = -lwin32u $(PTHREAD_LIBS) ++UNIX_LIBS = -lwin32u -lm $(PTHREAD_LIBS) + + C_SRCS = \ + loader.c \ +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index f680c130955..5867201e97b 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -3234,7 +3234,7 @@ class VkGenerator(object): + f.write(" * resolution; user_sz will contain the app's requested mode; and dst_blit\n") + f.write(" * will contain the area to blit the user image to in real coordinates.\n") + f.write(" * All parameters are optional. */\n") +- f.write(" VkBool32 (*query_fs_hack)(VkSurfaceKHR surface, VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter);\n") ++ f.write(" VkBool32 (*query_fs_hack)(VkSurfaceKHR surface, VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter, BOOL *fsr, float *sharpness);\n") + f.write("};\n\n") + + f.write("extern const struct vulkan_funcs * __wine_get_vulkan_driver(UINT version);\n\n") +diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c +index 34e37480c15..5996eefe677 100644 +--- a/dlls/winevulkan/vulkan.c ++++ b/dlls/winevulkan/vulkan.c +@@ -1941,29 +1941,819 @@ const uint32_t blit_comp_spv[] = { + 0x0000005b,0x00000021,0x00040063,0x00000056,0x0000005a,0x0000005b,0x000100fd,0x00010038 + }; + +-static VkResult create_pipeline(struct wine_device *device, struct wine_swapchain *swapchain, VkShaderModule shaderModule) ++/* ++#version 460 ++#extension GL_GOOGLE_include_directive: require ++ ++layout(local_size_x=8, local_size_y=8, local_size_z=1) in; ++ ++layout(binding = 0) uniform sampler2D texSampler; ++layout(binding = 1) uniform writeonly image2D outImage; ++ ++#define A_GPU 1 ++#define A_GLSL 1 ++//#include "ffx_a.h" ++#define FSR_EASU_F 1 ++AF4 FsrEasuRF(AF2 p){return AF4(textureGather(texSampler, p, 0));} ++AF4 FsrEasuGF(AF2 p){return AF4(textureGather(texSampler, p, 1));} ++AF4 FsrEasuBF(AF2 p){return AF4(textureGather(texSampler, p, 2));} ++//#include "ffx_fsr1.h" ++ ++layout(push_constant) uniform pushConstants { ++ uvec4 c1, c2, c3, c4; ++}; ++ ++ ++void main() ++{ ++ vec3 color; ++ ++ if (any(greaterThanEqual(gl_GlobalInvocationID.xy, c4.zw))) ++ return; ++ ++ FsrEasuF(color, uvec2(gl_GlobalInvocationID.xy), c1, c2, c3, c4); ++ ++ imageStore(outImage, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1.0)); ++} ++*/ ++const uint32_t fsr_easu_comp_spv[] = { ++ 0x07230203,0x00010000,0x0008000a,0x0000129e,0x00000000,0x00020011,0x00000001,0x00020011, ++ 0x00000038,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e, ++ 0x00000000,0x00000001,0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x000004ee, ++ 0x00060010,0x00000004,0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002, ++ 0x000001cc,0x000a0004,0x475f4c47,0x4c474f4f,0x70635f45,0x74735f70,0x5f656c79,0x656e696c, ++ 0x7269645f,0x69746365,0x00006576,0x00080004,0x475f4c47,0x4c474f4f,0x6e695f45,0x64756c63, ++ 0x69645f65,0x74636572,0x00657669,0x00040005,0x00000004,0x6e69616d,0x00000000,0x00050005, ++ 0x000000bc,0x53786574,0x6c706d61,0x00007265,0x00080005,0x000004ee,0x475f6c67,0x61626f6c, ++ 0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00060005,0x000004f1,0x68737570,0x736e6f43, ++ 0x746e6174,0x00000073,0x00040006,0x000004f1,0x00000000,0x00003163,0x00040006,0x000004f1, ++ 0x00000001,0x00003263,0x00040006,0x000004f1,0x00000002,0x00003363,0x00040006,0x000004f1, ++ 0x00000003,0x00003463,0x00030005,0x000004f3,0x00000000,0x00050005,0x00000517,0x4974756f, ++ 0x6567616d,0x00000000,0x00040047,0x000000bc,0x00000022,0x00000000,0x00040047,0x000000bc, ++ 0x00000021,0x00000000,0x00040047,0x000004ee,0x0000000b,0x0000001c,0x00050048,0x000004f1, ++ 0x00000000,0x00000023,0x00000000,0x00050048,0x000004f1,0x00000001,0x00000023,0x00000010, ++ 0x00050048,0x000004f1,0x00000002,0x00000023,0x00000020,0x00050048,0x000004f1,0x00000003, ++ 0x00000023,0x00000030,0x00030047,0x000004f1,0x00000002,0x00040047,0x00000517,0x00000022, ++ 0x00000000,0x00040047,0x00000517,0x00000021,0x00000001,0x00030047,0x00000517,0x00000019, ++ 0x00040047,0x00000523,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003, ++ 0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x0000000c,0x00000006,0x00000002, ++ 0x00040017,0x00000011,0x00000006,0x00000003,0x00040017,0x00000016,0x00000006,0x00000004, ++ 0x00040015,0x0000001b,0x00000020,0x00000000,0x00020014,0x0000004f,0x00040017,0x00000060, ++ 0x0000001b,0x00000002,0x00040017,0x00000062,0x0000001b,0x00000004,0x0004002b,0x00000006, ++ 0x00000093,0x3f800000,0x0004002b,0x00000006,0x0000009b,0x00000000,0x0004002b,0x0000001b, ++ 0x000000a3,0x7ef07ebb,0x0004002b,0x0000001b,0x000000ac,0x5f347d74,0x0004002b,0x0000001b, ++ 0x000000b1,0x00000001,0x00090019,0x000000b9,0x00000006,0x00000001,0x00000000,0x00000000, ++ 0x00000000,0x00000001,0x00000000,0x0003001b,0x000000ba,0x000000b9,0x00040020,0x000000bb, ++ 0x00000000,0x000000ba,0x0004003b,0x000000bb,0x000000bc,0x00000000,0x00040015,0x000000bf, ++ 0x00000020,0x00000001,0x0004002b,0x000000bf,0x000000c0,0x00000000,0x0004002b,0x000000bf, ++ 0x000000cb,0x00000001,0x0004002b,0x000000bf,0x000000d6,0x00000002,0x0004002b,0x0000001b, ++ 0x000000e0,0x00000000,0x0004002b,0x00000006,0x0000010d,0x3ecccccd,0x0004002b,0x00000006, ++ 0x00000112,0xbf800000,0x0004002b,0x00000006,0x00000123,0x3fc80000,0x0004002b,0x00000006, ++ 0x00000128,0xbf100000,0x0004002b,0x00000006,0x00000230,0x3f000000,0x0004002b,0x00000006, ++ 0x000002f5,0x38000000,0x0004002b,0x00000006,0x0000033e,0xbf000000,0x0004002b,0x00000006, ++ 0x00000348,0xbe947ae1,0x0005002c,0x0000000c,0x0000039c,0x0000009b,0x00000112,0x0005002c, ++ 0x0000000c,0x000003b7,0x00000093,0x00000112,0x0005002c,0x0000000c,0x000003d2,0x00000112, ++ 0x00000093,0x0005002c,0x0000000c,0x000003ed,0x0000009b,0x00000093,0x0005002c,0x0000000c, ++ 0x00000423,0x00000112,0x0000009b,0x0005002c,0x0000000c,0x0000043e,0x00000093,0x00000093, ++ 0x0004002b,0x00000006,0x00000459,0x40000000,0x0005002c,0x0000000c,0x0000045a,0x00000459, ++ 0x00000093,0x0005002c,0x0000000c,0x00000475,0x00000459,0x0000009b,0x0005002c,0x0000000c, ++ 0x00000490,0x00000093,0x0000009b,0x0005002c,0x0000000c,0x000004ab,0x00000093,0x00000459, ++ 0x0005002c,0x0000000c,0x000004c6,0x0000009b,0x00000459,0x00040017,0x000004ec,0x0000001b, ++ 0x00000003,0x00040020,0x000004ed,0x00000001,0x000004ec,0x0004003b,0x000004ed,0x000004ee, ++ 0x00000001,0x0006001e,0x000004f1,0x00000062,0x00000062,0x00000062,0x00000062,0x00040020, ++ 0x000004f2,0x00000009,0x000004f1,0x0004003b,0x000004f2,0x000004f3,0x00000009,0x0004002b, ++ 0x000000bf,0x000004f4,0x00000003,0x00040020,0x000004f5,0x00000009,0x00000062,0x00040017, ++ 0x000004f9,0x0000004f,0x00000002,0x00090019,0x00000515,0x00000006,0x00000001,0x00000000, ++ 0x00000000,0x00000000,0x00000002,0x00000000,0x00040020,0x00000516,0x00000000,0x00000515, ++ 0x0004003b,0x00000516,0x00000517,0x00000000,0x00040017,0x0000051b,0x000000bf,0x00000002, ++ 0x0004002b,0x0000001b,0x00000522,0x00000008,0x0006002c,0x000004ec,0x00000523,0x00000522, ++ 0x00000522,0x000000b1,0x0007002c,0x00000016,0x0000127a,0x00000230,0x00000230,0x00000230, ++ 0x00000230,0x00030001,0x0000000c,0x0000129d,0x00050036,0x00000002,0x00000004,0x00000000, ++ 0x00000003,0x000200f8,0x00000005,0x000300f7,0x00000524,0x00000000,0x000300fb,0x000000e0, ++ 0x00000525,0x000200f8,0x00000525,0x0004003d,0x000004ec,0x000004ef,0x000004ee,0x0007004f, ++ 0x00000060,0x000004f0,0x000004ef,0x000004ef,0x00000000,0x00000001,0x00050041,0x000004f5, ++ 0x000004f6,0x000004f3,0x000004f4,0x0004003d,0x00000062,0x000004f7,0x000004f6,0x0007004f, ++ 0x00000060,0x000004f8,0x000004f7,0x000004f7,0x00000002,0x00000003,0x000500ae,0x000004f9, ++ 0x000004fa,0x000004f0,0x000004f8,0x0004009a,0x0000004f,0x000004fb,0x000004fa,0x000300f7, ++ 0x000004fd,0x00000000,0x000400fa,0x000004fb,0x000004fc,0x000004fd,0x000200f8,0x000004fc, ++ 0x000200f9,0x00000524,0x000200f8,0x000004fd,0x00050051,0x0000001b,0x00000502,0x000004ef, ++ 0x00000000,0x00050051,0x0000001b,0x00000503,0x000004ef,0x00000001,0x00050050,0x00000060, ++ 0x00000504,0x00000502,0x00000503,0x00050041,0x000004f5,0x00000508,0x000004f3,0x000000c0, ++ 0x0004003d,0x00000062,0x00000509,0x00000508,0x00050041,0x000004f5,0x0000050b,0x000004f3, ++ 0x000000cb,0x0004003d,0x00000062,0x0000050c,0x0000050b,0x00050041,0x000004f5,0x0000050e, ++ 0x000004f3,0x000000d6,0x0004003d,0x00000062,0x0000050f,0x0000050e,0x00040070,0x0000000c, ++ 0x00000618,0x00000504,0x00050051,0x0000001b,0x0000061b,0x00000509,0x00000000,0x00050051, ++ 0x0000001b,0x0000061c,0x00000509,0x00000001,0x00050050,0x00000060,0x0000061d,0x0000061b, ++ 0x0000061c,0x0004007c,0x0000000c,0x0000061e,0x0000061d,0x00050085,0x0000000c,0x0000061f, ++ 0x00000618,0x0000061e,0x00050051,0x0000001b,0x00000622,0x00000509,0x00000002,0x00050051, ++ 0x0000001b,0x00000623,0x00000509,0x00000003,0x00050050,0x00000060,0x00000624,0x00000622, ++ 0x00000623,0x0004007c,0x0000000c,0x00000625,0x00000624,0x00050081,0x0000000c,0x00000626, ++ 0x0000061f,0x00000625,0x0006000c,0x0000000c,0x00000628,0x00000001,0x00000008,0x00000626, ++ 0x00050083,0x0000000c,0x0000062b,0x00000626,0x00000628,0x00050051,0x0000001b,0x0000062f, ++ 0x0000050c,0x00000000,0x00050051,0x0000001b,0x00000630,0x0000050c,0x00000001,0x00050050, ++ 0x00000060,0x00000631,0x0000062f,0x00000630,0x0004007c,0x0000000c,0x00000632,0x00000631, ++ 0x00050085,0x0000000c,0x00000633,0x00000628,0x00000632,0x00050051,0x0000001b,0x00000636, ++ 0x0000050c,0x00000002,0x00050051,0x0000001b,0x00000637,0x0000050c,0x00000003,0x00050050, ++ 0x00000060,0x00000638,0x00000636,0x00000637,0x0004007c,0x0000000c,0x00000639,0x00000638, ++ 0x00050081,0x0000000c,0x0000063a,0x00000633,0x00000639,0x00050051,0x0000001b,0x0000063e, ++ 0x0000050f,0x00000000,0x00050051,0x0000001b,0x0000063f,0x0000050f,0x00000001,0x00050050, ++ 0x00000060,0x00000640,0x0000063e,0x0000063f,0x0004007c,0x0000000c,0x00000641,0x00000640, ++ 0x00050081,0x0000000c,0x00000642,0x0000063a,0x00000641,0x00050051,0x0000001b,0x00000646, ++ 0x0000050f,0x00000002,0x00050051,0x0000001b,0x00000647,0x0000050f,0x00000003,0x00050050, ++ 0x00000060,0x00000648,0x00000646,0x00000647,0x0004007c,0x0000000c,0x00000649,0x00000648, ++ 0x00050081,0x0000000c,0x0000064a,0x0000063a,0x00000649,0x00050051,0x0000001b,0x0000064e, ++ 0x000004f7,0x00000000,0x00050051,0x0000001b,0x0000064f,0x000004f7,0x00000001,0x00050050, ++ 0x00000060,0x00000650,0x0000064e,0x0000064f,0x0004007c,0x0000000c,0x00000651,0x00000650, ++ 0x00050081,0x0000000c,0x00000652,0x0000063a,0x00000651,0x0004003d,0x000000ba,0x00000845, ++ 0x000000bc,0x00060060,0x00000016,0x00000847,0x00000845,0x0000063a,0x000000c0,0x00060060, ++ 0x00000016,0x00000851,0x00000845,0x0000063a,0x000000cb,0x00060060,0x00000016,0x0000085b, ++ 0x00000845,0x0000063a,0x000000d6,0x00060060,0x00000016,0x00000865,0x00000845,0x00000642, ++ 0x000000c0,0x00060060,0x00000016,0x0000086f,0x00000845,0x00000642,0x000000cb,0x00060060, ++ 0x00000016,0x00000879,0x00000845,0x00000642,0x000000d6,0x00060060,0x00000016,0x00000883, ++ 0x00000845,0x0000064a,0x000000c0,0x00060060,0x00000016,0x0000088d,0x00000845,0x0000064a, ++ 0x000000cb,0x00060060,0x00000016,0x00000897,0x00000845,0x0000064a,0x000000d6,0x00060060, ++ 0x00000016,0x000008a1,0x00000845,0x00000652,0x000000c0,0x00060060,0x00000016,0x000008ab, ++ 0x00000845,0x00000652,0x000000cb,0x00060060,0x00000016,0x000008b5,0x00000845,0x00000652, ++ 0x000000d6,0x00050085,0x00000016,0x0000066d,0x0000085b,0x0000127a,0x00050085,0x00000016, ++ 0x00000670,0x00000847,0x0000127a,0x00050081,0x00000016,0x00000672,0x00000670,0x00000851, ++ 0x00050081,0x00000016,0x00000673,0x0000066d,0x00000672,0x00050085,0x00000016,0x00000676, ++ 0x00000879,0x0000127a,0x00050085,0x00000016,0x00000679,0x00000865,0x0000127a,0x00050081, ++ 0x00000016,0x0000067b,0x00000679,0x0000086f,0x00050081,0x00000016,0x0000067c,0x00000676, ++ 0x0000067b,0x00050085,0x00000016,0x0000067f,0x00000897,0x0000127a,0x00050085,0x00000016, ++ 0x00000682,0x00000883,0x0000127a,0x00050081,0x00000016,0x00000684,0x00000682,0x0000088d, ++ 0x00050081,0x00000016,0x00000685,0x0000067f,0x00000684,0x00050085,0x00000016,0x00000688, ++ 0x000008b5,0x0000127a,0x00050085,0x00000016,0x0000068b,0x000008a1,0x0000127a,0x00050081, ++ 0x00000016,0x0000068d,0x0000068b,0x000008ab,0x00050081,0x00000016,0x0000068e,0x00000688, ++ 0x0000068d,0x00050051,0x00000006,0x00000690,0x00000673,0x00000000,0x00050051,0x00000006, ++ 0x00000692,0x00000673,0x00000001,0x00050051,0x00000006,0x00000694,0x0000067c,0x00000000, ++ 0x00050051,0x00000006,0x00000696,0x0000067c,0x00000001,0x00050051,0x00000006,0x00000698, ++ 0x0000067c,0x00000002,0x00050051,0x00000006,0x0000069a,0x0000067c,0x00000003,0x00050051, ++ 0x00000006,0x0000069c,0x00000685,0x00000000,0x00050051,0x00000006,0x0000069e,0x00000685, ++ 0x00000001,0x00050051,0x00000006,0x000006a0,0x00000685,0x00000002,0x00050051,0x00000006, ++ 0x000006a2,0x00000685,0x00000003,0x00050051,0x00000006,0x000006a4,0x0000068e,0x00000002, ++ 0x00050051,0x00000006,0x000006a6,0x0000068e,0x00000003,0x00050051,0x00000006,0x00000913, ++ 0x0000062b,0x00000000,0x00050083,0x00000006,0x00000914,0x00000093,0x00000913,0x00050051, ++ 0x00000006,0x00000917,0x0000062b,0x00000001,0x00050083,0x00000006,0x00000918,0x00000093, ++ 0x00000917,0x00050085,0x00000006,0x00000919,0x00000914,0x00000918,0x00050083,0x00000006, ++ 0x00000939,0x000006a2,0x00000698,0x00050083,0x00000006,0x0000093c,0x00000698,0x0000069a, ++ 0x0006000c,0x00000006,0x0000093e,0x00000001,0x00000004,0x00000939,0x0006000c,0x00000006, ++ 0x00000940,0x00000001,0x00000004,0x0000093c,0x0007000c,0x00000006,0x00000941,0x00000001, ++ 0x00000028,0x0000093e,0x00000940,0x0004007c,0x0000001b,0x00000993,0x00000941,0x00050082, ++ 0x0000001b,0x00000994,0x000000a3,0x00000993,0x0004007c,0x00000006,0x00000995,0x00000994, ++ 0x00050083,0x00000006,0x00000946,0x000006a2,0x0000069a,0x00050085,0x00000006,0x00000949, ++ 0x00000946,0x00000919,0x0006000c,0x00000006,0x0000094f,0x00000001,0x00000004,0x00000946, ++ 0x00050085,0x00000006,0x00000951,0x0000094f,0x00000995,0x0008000c,0x00000006,0x000009a0, ++ 0x00000001,0x0000002b,0x00000951,0x0000009b,0x00000093,0x00050085,0x00000006,0x00000955, ++ 0x000009a0,0x000009a0,0x00050085,0x00000006,0x00000958,0x00000955,0x00000919,0x00050083, ++ 0x00000006,0x0000095d,0x00000696,0x00000698,0x00050083,0x00000006,0x00000960,0x00000698, ++ 0x00000690,0x0006000c,0x00000006,0x00000962,0x00000001,0x00000004,0x0000095d,0x0006000c, ++ 0x00000006,0x00000964,0x00000001,0x00000004,0x00000960,0x0007000c,0x00000006,0x00000965, ++ 0x00000001,0x00000028,0x00000962,0x00000964,0x0004007c,0x0000001b,0x000009ac,0x00000965, ++ 0x00050082,0x0000001b,0x000009ad,0x000000a3,0x000009ac,0x0004007c,0x00000006,0x000009ae, ++ 0x000009ad,0x00050083,0x00000006,0x0000096a,0x00000696,0x00000690,0x00050085,0x00000006, ++ 0x0000096d,0x0000096a,0x00000919,0x0006000c,0x00000006,0x00000973,0x00000001,0x00000004, ++ 0x0000096a,0x00050085,0x00000006,0x00000975,0x00000973,0x000009ae,0x0008000c,0x00000006, ++ 0x000009b9,0x00000001,0x0000002b,0x00000975,0x0000009b,0x00000093,0x00050085,0x00000006, ++ 0x00000979,0x000009b9,0x000009b9,0x00050085,0x00000006,0x0000097c,0x00000979,0x00000919, ++ 0x00050081,0x00000006,0x0000097e,0x00000958,0x0000097c,0x00050085,0x00000006,0x000009e8, ++ 0x00000913,0x00000918,0x00050083,0x00000006,0x000009fe,0x000006a0,0x000006a2,0x0006000c, ++ 0x00000006,0x00000a03,0x00000001,0x00000004,0x000009fe,0x0007000c,0x00000006,0x00000a06, ++ 0x00000001,0x00000028,0x00000a03,0x0000093e,0x0004007c,0x0000001b,0x00000a58,0x00000a06, ++ 0x00050082,0x0000001b,0x00000a59,0x000000a3,0x00000a58,0x0004007c,0x00000006,0x00000a5a, ++ 0x00000a59,0x00050083,0x00000006,0x00000a0b,0x000006a0,0x00000698,0x00050085,0x00000006, ++ 0x00000a0e,0x00000a0b,0x000009e8,0x00050081,0x00000006,0x00000a11,0x00000949,0x00000a0e, ++ 0x0006000c,0x00000006,0x00000a14,0x00000001,0x00000004,0x00000a0b,0x00050085,0x00000006, ++ 0x00000a16,0x00000a14,0x00000a5a,0x0008000c,0x00000006,0x00000a65,0x00000001,0x0000002b, ++ 0x00000a16,0x0000009b,0x00000093,0x00050085,0x00000006,0x00000a1a,0x00000a65,0x00000a65, ++ 0x00050085,0x00000006,0x00000a1d,0x00000a1a,0x000009e8,0x00050081,0x00000006,0x00000a1f, ++ 0x0000097e,0x00000a1d,0x00050083,0x00000006,0x00000a22,0x0000069c,0x000006a2,0x00050083, ++ 0x00000006,0x00000a25,0x000006a2,0x00000692,0x0006000c,0x00000006,0x00000a27,0x00000001, ++ 0x00000004,0x00000a22,0x0006000c,0x00000006,0x00000a29,0x00000001,0x00000004,0x00000a25, ++ 0x0007000c,0x00000006,0x00000a2a,0x00000001,0x00000028,0x00000a27,0x00000a29,0x0004007c, ++ 0x0000001b,0x00000a71,0x00000a2a,0x00050082,0x0000001b,0x00000a72,0x000000a3,0x00000a71, ++ 0x0004007c,0x00000006,0x00000a73,0x00000a72,0x00050083,0x00000006,0x00000a2f,0x0000069c, ++ 0x00000692,0x00050085,0x00000006,0x00000a32,0x00000a2f,0x000009e8,0x00050081,0x00000006, ++ 0x00000a35,0x0000096d,0x00000a32,0x0006000c,0x00000006,0x00000a38,0x00000001,0x00000004, ++ 0x00000a2f,0x00050085,0x00000006,0x00000a3a,0x00000a38,0x00000a73,0x0008000c,0x00000006, ++ 0x00000a7e,0x00000001,0x0000002b,0x00000a3a,0x0000009b,0x00000093,0x00050085,0x00000006, ++ 0x00000a3e,0x00000a7e,0x00000a7e,0x00050085,0x00000006,0x00000a41,0x00000a3e,0x000009e8, ++ 0x00050081,0x00000006,0x00000a43,0x00000a1f,0x00000a41,0x00050085,0x00000006,0x00000ab7, ++ 0x00000914,0x00000917,0x00050083,0x00000006,0x00000ac3,0x0000069c,0x00000696,0x00050083, ++ 0x00000006,0x00000ac6,0x00000696,0x00000694,0x0006000c,0x00000006,0x00000ac8,0x00000001, ++ 0x00000004,0x00000ac3,0x0006000c,0x00000006,0x00000aca,0x00000001,0x00000004,0x00000ac6, ++ 0x0007000c,0x00000006,0x00000acb,0x00000001,0x00000028,0x00000ac8,0x00000aca,0x0004007c, ++ 0x0000001b,0x00000b1d,0x00000acb,0x00050082,0x0000001b,0x00000b1e,0x000000a3,0x00000b1d, ++ 0x0004007c,0x00000006,0x00000b1f,0x00000b1e,0x00050083,0x00000006,0x00000ad0,0x0000069c, ++ 0x00000694,0x00050085,0x00000006,0x00000ad3,0x00000ad0,0x00000ab7,0x00050081,0x00000006, ++ 0x00000ad6,0x00000a11,0x00000ad3,0x0006000c,0x00000006,0x00000ad9,0x00000001,0x00000004, ++ 0x00000ad0,0x00050085,0x00000006,0x00000adb,0x00000ad9,0x00000b1f,0x0008000c,0x00000006, ++ 0x00000b2a,0x00000001,0x0000002b,0x00000adb,0x0000009b,0x00000093,0x00050085,0x00000006, ++ 0x00000adf,0x00000b2a,0x00000b2a,0x00050085,0x00000006,0x00000ae2,0x00000adf,0x00000ab7, ++ 0x00050081,0x00000006,0x00000ae4,0x00000a43,0x00000ae2,0x00050083,0x00000006,0x00000ae7, ++ 0x000006a6,0x00000696,0x0006000c,0x00000006,0x00000aec,0x00000001,0x00000004,0x00000ae7, ++ 0x0007000c,0x00000006,0x00000aef,0x00000001,0x00000028,0x00000aec,0x00000962,0x0004007c, ++ 0x0000001b,0x00000b36,0x00000aef,0x00050082,0x0000001b,0x00000b37,0x000000a3,0x00000b36, ++ 0x0004007c,0x00000006,0x00000b38,0x00000b37,0x00050083,0x00000006,0x00000af4,0x000006a6, ++ 0x00000698,0x00050085,0x00000006,0x00000af7,0x00000af4,0x00000ab7,0x00050081,0x00000006, ++ 0x00000afa,0x00000a35,0x00000af7,0x0006000c,0x00000006,0x00000afd,0x00000001,0x00000004, ++ 0x00000af4,0x00050085,0x00000006,0x00000aff,0x00000afd,0x00000b38,0x0008000c,0x00000006, ++ 0x00000b43,0x00000001,0x0000002b,0x00000aff,0x0000009b,0x00000093,0x00050085,0x00000006, ++ 0x00000b03,0x00000b43,0x00000b43,0x00050085,0x00000006,0x00000b06,0x00000b03,0x00000ab7, ++ 0x00050081,0x00000006,0x00000b08,0x00000ae4,0x00000b06,0x00050085,0x00000006,0x00000b84, ++ 0x00000913,0x00000917,0x00050083,0x00000006,0x00000b88,0x0000069e,0x0000069c,0x0006000c, ++ 0x00000006,0x00000b8d,0x00000001,0x00000004,0x00000b88,0x0007000c,0x00000006,0x00000b90, ++ 0x00000001,0x00000028,0x00000b8d,0x00000ac8,0x0004007c,0x0000001b,0x00000be2,0x00000b90, ++ 0x00050082,0x0000001b,0x00000be3,0x000000a3,0x00000be2,0x0004007c,0x00000006,0x00000be4, ++ 0x00000be3,0x00050083,0x00000006,0x00000b95,0x0000069e,0x00000696,0x00050085,0x00000006, ++ 0x00000b98,0x00000b95,0x00000b84,0x00050081,0x00000006,0x00000b9b,0x00000ad6,0x00000b98, ++ 0x00060052,0x0000000c,0x0000116f,0x00000b9b,0x0000129d,0x00000000,0x0006000c,0x00000006, ++ 0x00000b9e,0x00000001,0x00000004,0x00000b95,0x00050085,0x00000006,0x00000ba0,0x00000b9e, ++ 0x00000be4,0x0008000c,0x00000006,0x00000bef,0x00000001,0x0000002b,0x00000ba0,0x0000009b, ++ 0x00000093,0x00050085,0x00000006,0x00000ba4,0x00000bef,0x00000bef,0x00050085,0x00000006, ++ 0x00000ba7,0x00000ba4,0x00000b84,0x00050081,0x00000006,0x00000ba9,0x00000b08,0x00000ba7, ++ 0x00050083,0x00000006,0x00000bac,0x000006a4,0x0000069c,0x0006000c,0x00000006,0x00000bb1, ++ 0x00000001,0x00000004,0x00000bac,0x0007000c,0x00000006,0x00000bb4,0x00000001,0x00000028, ++ 0x00000bb1,0x00000a27,0x0004007c,0x0000001b,0x00000bfb,0x00000bb4,0x00050082,0x0000001b, ++ 0x00000bfc,0x000000a3,0x00000bfb,0x0004007c,0x00000006,0x00000bfd,0x00000bfc,0x00050083, ++ 0x00000006,0x00000bb9,0x000006a4,0x000006a2,0x00050085,0x00000006,0x00000bbc,0x00000bb9, ++ 0x00000b84,0x00050081,0x00000006,0x00000bbf,0x00000afa,0x00000bbc,0x00060052,0x0000000c, ++ 0x00001172,0x00000bbf,0x0000116f,0x00000001,0x0006000c,0x00000006,0x00000bc2,0x00000001, ++ 0x00000004,0x00000bb9,0x00050085,0x00000006,0x00000bc4,0x00000bc2,0x00000bfd,0x0008000c, ++ 0x00000006,0x00000c08,0x00000001,0x0000002b,0x00000bc4,0x0000009b,0x00000093,0x00050085, ++ 0x00000006,0x00000bc8,0x00000c08,0x00000c08,0x00050085,0x00000006,0x00000bcb,0x00000bc8, ++ 0x00000b84,0x00050081,0x00000006,0x00000bcd,0x00000ba9,0x00000bcb,0x00050085,0x0000000c, ++ 0x000006d7,0x00001172,0x00001172,0x00050051,0x00000006,0x000006d9,0x000006d7,0x00000000, ++ 0x00050051,0x00000006,0x000006db,0x000006d7,0x00000001,0x00050081,0x00000006,0x000006dc, ++ 0x000006d9,0x000006db,0x000500b8,0x0000004f,0x000006df,0x000006dc,0x000002f5,0x0004007c, ++ 0x0000001b,0x00000c18,0x000006dc,0x000500c2,0x0000001b,0x00000c1a,0x00000c18,0x000000b1, ++ 0x00050082,0x0000001b,0x00000c1b,0x000000ac,0x00000c1a,0x0004007c,0x00000006,0x00000c1c, ++ 0x00000c1b,0x000200f9,0x000006e7,0x000200f8,0x000006e7,0x000600a9,0x00000006,0x0000129c, ++ 0x000006df,0x00000093,0x00000c1c,0x000300f7,0x000006ef,0x00000000,0x000400fa,0x000006df, ++ 0x000006ea,0x000006ec,0x000200f8,0x000006ec,0x000200f9,0x000006ef,0x000200f8,0x000006ea, ++ 0x000200f9,0x000006ef,0x000200f8,0x000006ef,0x000700f5,0x00000006,0x0000127e,0x00000b9b, ++ 0x000006ec,0x00000093,0x000006ea,0x00060052,0x0000000c,0x00001177,0x0000127e,0x00001172, ++ 0x00000000,0x00050050,0x0000000c,0x00000c2d,0x0000129c,0x0000129c,0x00050085,0x0000000c, ++ 0x000006f5,0x00001177,0x00000c2d,0x00050085,0x00000006,0x000006f8,0x00000bcd,0x00000230, ++ 0x00050085,0x00000006,0x000006fb,0x000006f8,0x000006f8,0x00050051,0x00000006,0x000006fd, ++ 0x000006f5,0x00000000,0x00050085,0x00000006,0x00000700,0x000006fd,0x000006fd,0x00050051, ++ 0x00000006,0x00000702,0x000006f5,0x00000001,0x00050085,0x00000006,0x00000705,0x00000702, ++ 0x00000702,0x00050081,0x00000006,0x00000706,0x00000700,0x00000705,0x0006000c,0x00000006, ++ 0x00000709,0x00000001,0x00000004,0x000006fd,0x0006000c,0x00000006,0x0000070c,0x00000001, ++ 0x00000004,0x00000702,0x0007000c,0x00000006,0x0000070d,0x00000001,0x00000028,0x00000709, ++ 0x0000070c,0x0004007c,0x0000001b,0x00000c36,0x0000070d,0x00050082,0x0000001b,0x00000c37, ++ 0x000000a3,0x00000c36,0x0004007c,0x00000006,0x00000c38,0x00000c37,0x00050085,0x00000006, ++ 0x0000070f,0x00000706,0x00000c38,0x00050083,0x00000006,0x00000713,0x0000070f,0x00000093, ++ 0x00050085,0x00000006,0x00000715,0x00000713,0x000006fb,0x00050081,0x00000006,0x00000716, ++ 0x00000093,0x00000715,0x00050085,0x00000006,0x0000071a,0x0000033e,0x000006fb,0x00050081, ++ 0x00000006,0x0000071b,0x00000093,0x0000071a,0x00050050,0x0000000c,0x0000071c,0x00000716, ++ 0x0000071b,0x00050085,0x00000006,0x00000720,0x00000348,0x000006fb,0x00050081,0x00000006, ++ 0x00000721,0x00000230,0x00000720,0x0004007c,0x0000001b,0x00000c53,0x00000721,0x00050082, ++ 0x0000001b,0x00000c54,0x000000a3,0x00000c53,0x0004007c,0x00000006,0x00000c55,0x00000c54, ++ 0x00050051,0x00000006,0x00000725,0x00000865,0x00000002,0x00050051,0x00000006,0x00000727, ++ 0x0000086f,0x00000002,0x00050051,0x00000006,0x00000729,0x00000879,0x00000002,0x00060050, ++ 0x00000011,0x0000072a,0x00000725,0x00000727,0x00000729,0x00050051,0x00000006,0x0000072c, ++ 0x00000883,0x00000003,0x00050051,0x00000006,0x0000072e,0x0000088d,0x00000003,0x00050051, ++ 0x00000006,0x00000730,0x00000897,0x00000003,0x00060050,0x00000011,0x00000731,0x0000072c, ++ 0x0000072e,0x00000730,0x00050051,0x00000006,0x00000733,0x00000865,0x00000001,0x00050051, ++ 0x00000006,0x00000735,0x0000086f,0x00000001,0x00050051,0x00000006,0x00000737,0x00000879, ++ 0x00000001,0x00060050,0x00000011,0x00000738,0x00000733,0x00000735,0x00000737,0x0007000c, ++ 0x00000011,0x00000c5e,0x00000001,0x00000025,0x00000731,0x00000738,0x0007000c,0x00000011, ++ 0x00000c5f,0x00000001,0x00000025,0x0000072a,0x00000c5e,0x00050051,0x00000006,0x0000073b, ++ 0x00000883,0x00000000,0x00050051,0x00000006,0x0000073d,0x0000088d,0x00000000,0x00050051, ++ 0x00000006,0x0000073f,0x00000897,0x00000000,0x00060050,0x00000011,0x00000740,0x0000073b, ++ 0x0000073d,0x0000073f,0x0007000c,0x00000011,0x00000741,0x00000001,0x00000025,0x00000c5f, ++ 0x00000740,0x0007000c,0x00000011,0x00000c65,0x00000001,0x00000028,0x00000731,0x00000738, ++ 0x0007000c,0x00000011,0x00000c66,0x00000001,0x00000028,0x0000072a,0x00000c65,0x0007000c, ++ 0x00000011,0x0000075f,0x00000001,0x00000028,0x00000c66,0x00000740,0x00050083,0x0000000c, ++ 0x00000763,0x0000039c,0x0000062b,0x00050051,0x00000006,0x00000765,0x00000847,0x00000000, ++ 0x00050051,0x00000006,0x00000767,0x00000851,0x00000000,0x00050051,0x00000006,0x00000769, ++ 0x0000085b,0x00000000,0x00060050,0x00000011,0x0000076a,0x00000765,0x00000767,0x00000769, ++ 0x00050051,0x00000006,0x00000c7c,0x00000763,0x00000000,0x00050085,0x00000006,0x00000c7f, ++ 0x00000c7c,0x000006fd,0x00050051,0x00000006,0x00000c81,0x00000763,0x00000001,0x00050085, ++ 0x00000006,0x00000c84,0x00000c81,0x00000702,0x00050081,0x00000006,0x00000c85,0x00000c7f, ++ 0x00000c84,0x00060052,0x0000000c,0x0000119e,0x00000c85,0x0000129d,0x00000000,0x0004007f, ++ 0x00000006,0x00000c8b,0x00000702,0x00050085,0x00000006,0x00000c8c,0x00000c7c,0x00000c8b, ++ 0x00050085,0x00000006,0x00000c91,0x00000c81,0x000006fd,0x00050081,0x00000006,0x00000c92, ++ 0x00000c8c,0x00000c91,0x00060052,0x0000000c,0x000011a4,0x00000c92,0x0000119e,0x00000001, ++ 0x00050085,0x0000000c,0x00000c96,0x000011a4,0x0000071c,0x00050051,0x00000006,0x00000c98, ++ 0x00000c96,0x00000000,0x00050085,0x00000006,0x00000c9b,0x00000c98,0x00000c98,0x00050051, ++ 0x00000006,0x00000c9d,0x00000c96,0x00000001,0x00050085,0x00000006,0x00000ca0,0x00000c9d, ++ 0x00000c9d,0x00050081,0x00000006,0x00000ca1,0x00000c9b,0x00000ca0,0x0007000c,0x00000006, ++ 0x00000ca4,0x00000001,0x00000025,0x00000ca1,0x00000c55,0x00050085,0x00000006,0x00000ca7, ++ 0x0000010d,0x00000ca4,0x00050081,0x00000006,0x00000ca9,0x00000ca7,0x00000112,0x00050085, ++ 0x00000006,0x00000cac,0x00000721,0x00000ca4,0x00050081,0x00000006,0x00000cae,0x00000cac, ++ 0x00000112,0x00050085,0x00000006,0x00000cb1,0x00000ca9,0x00000ca9,0x00050085,0x00000006, ++ 0x00000cb4,0x00000cae,0x00000cae,0x00050085,0x00000006,0x00000cb7,0x00000123,0x00000cb1, ++ 0x00050081,0x00000006,0x00000cb9,0x00000cb7,0x00000128,0x00050085,0x00000006,0x00000cbc, ++ 0x00000cb9,0x00000cb4,0x0005008e,0x00000011,0x00000cbf,0x0000076a,0x00000cbc,0x00050083, ++ 0x0000000c,0x00000775,0x000003b7,0x0000062b,0x00050051,0x00000006,0x00000777,0x00000847, ++ 0x00000001,0x00050051,0x00000006,0x00000779,0x00000851,0x00000001,0x00050051,0x00000006, ++ 0x0000077b,0x0000085b,0x00000001,0x00060050,0x00000011,0x0000077c,0x00000777,0x00000779, ++ 0x0000077b,0x00050051,0x00000006,0x00000ce0,0x00000775,0x00000000,0x00050085,0x00000006, ++ 0x00000ce3,0x00000ce0,0x000006fd,0x00050051,0x00000006,0x00000ce5,0x00000775,0x00000001, ++ 0x00050085,0x00000006,0x00000ce8,0x00000ce5,0x00000702,0x00050081,0x00000006,0x00000ce9, ++ 0x00000ce3,0x00000ce8,0x00060052,0x0000000c,0x000011b1,0x00000ce9,0x0000129d,0x00000000, ++ 0x00050085,0x00000006,0x00000cf0,0x00000ce0,0x00000c8b,0x00050085,0x00000006,0x00000cf5, ++ 0x00000ce5,0x000006fd,0x00050081,0x00000006,0x00000cf6,0x00000cf0,0x00000cf5,0x00060052, ++ 0x0000000c,0x000011b7,0x00000cf6,0x000011b1,0x00000001,0x00050085,0x0000000c,0x00000cfa, ++ 0x000011b7,0x0000071c,0x00050051,0x00000006,0x00000cfc,0x00000cfa,0x00000000,0x00050085, ++ 0x00000006,0x00000cff,0x00000cfc,0x00000cfc,0x00050051,0x00000006,0x00000d01,0x00000cfa, ++ 0x00000001,0x00050085,0x00000006,0x00000d04,0x00000d01,0x00000d01,0x00050081,0x00000006, ++ 0x00000d05,0x00000cff,0x00000d04,0x0007000c,0x00000006,0x00000d08,0x00000001,0x00000025, ++ 0x00000d05,0x00000c55,0x00050085,0x00000006,0x00000d0b,0x0000010d,0x00000d08,0x00050081, ++ 0x00000006,0x00000d0d,0x00000d0b,0x00000112,0x00050085,0x00000006,0x00000d10,0x00000721, ++ 0x00000d08,0x00050081,0x00000006,0x00000d12,0x00000d10,0x00000112,0x00050085,0x00000006, ++ 0x00000d15,0x00000d0d,0x00000d0d,0x00050085,0x00000006,0x00000d18,0x00000d12,0x00000d12, ++ 0x00050085,0x00000006,0x00000d1b,0x00000123,0x00000d15,0x00050081,0x00000006,0x00000d1d, ++ 0x00000d1b,0x00000128,0x00050085,0x00000006,0x00000d20,0x00000d1d,0x00000d18,0x0005008e, ++ 0x00000011,0x00000d23,0x0000077c,0x00000d20,0x00050081,0x00000011,0x00000d25,0x00000cbf, ++ 0x00000d23,0x00050081,0x00000006,0x00000d28,0x00000cbc,0x00000d20,0x00050083,0x0000000c, ++ 0x00000787,0x000003d2,0x0000062b,0x00050051,0x00000006,0x00000789,0x00000865,0x00000000, ++ 0x00050051,0x00000006,0x0000078b,0x0000086f,0x00000000,0x00050051,0x00000006,0x0000078d, ++ 0x00000879,0x00000000,0x00060050,0x00000011,0x0000078e,0x00000789,0x0000078b,0x0000078d, ++ 0x00050051,0x00000006,0x00000d44,0x00000787,0x00000000,0x00050085,0x00000006,0x00000d47, ++ 0x00000d44,0x000006fd,0x00050051,0x00000006,0x00000d49,0x00000787,0x00000001,0x00050085, ++ 0x00000006,0x00000d4c,0x00000d49,0x00000702,0x00050081,0x00000006,0x00000d4d,0x00000d47, ++ 0x00000d4c,0x00060052,0x0000000c,0x000011c4,0x00000d4d,0x0000129d,0x00000000,0x00050085, ++ 0x00000006,0x00000d54,0x00000d44,0x00000c8b,0x00050085,0x00000006,0x00000d59,0x00000d49, ++ 0x000006fd,0x00050081,0x00000006,0x00000d5a,0x00000d54,0x00000d59,0x00060052,0x0000000c, ++ 0x000011ca,0x00000d5a,0x000011c4,0x00000001,0x00050085,0x0000000c,0x00000d5e,0x000011ca, ++ 0x0000071c,0x00050051,0x00000006,0x00000d60,0x00000d5e,0x00000000,0x00050085,0x00000006, ++ 0x00000d63,0x00000d60,0x00000d60,0x00050051,0x00000006,0x00000d65,0x00000d5e,0x00000001, ++ 0x00050085,0x00000006,0x00000d68,0x00000d65,0x00000d65,0x00050081,0x00000006,0x00000d69, ++ 0x00000d63,0x00000d68,0x0007000c,0x00000006,0x00000d6c,0x00000001,0x00000025,0x00000d69, ++ 0x00000c55,0x00050085,0x00000006,0x00000d6f,0x0000010d,0x00000d6c,0x00050081,0x00000006, ++ 0x00000d71,0x00000d6f,0x00000112,0x00050085,0x00000006,0x00000d74,0x00000721,0x00000d6c, ++ 0x00050081,0x00000006,0x00000d76,0x00000d74,0x00000112,0x00050085,0x00000006,0x00000d79, ++ 0x00000d71,0x00000d71,0x00050085,0x00000006,0x00000d7c,0x00000d76,0x00000d76,0x00050085, ++ 0x00000006,0x00000d7f,0x00000123,0x00000d79,0x00050081,0x00000006,0x00000d81,0x00000d7f, ++ 0x00000128,0x00050085,0x00000006,0x00000d84,0x00000d81,0x00000d7c,0x0005008e,0x00000011, ++ 0x00000d87,0x0000078e,0x00000d84,0x00050081,0x00000011,0x00000d89,0x00000d25,0x00000d87, ++ 0x00050081,0x00000006,0x00000d8c,0x00000d28,0x00000d84,0x00050083,0x0000000c,0x00000799, ++ 0x000003ed,0x0000062b,0x00050051,0x00000006,0x00000da8,0x00000799,0x00000000,0x00050085, ++ 0x00000006,0x00000dab,0x00000da8,0x000006fd,0x00050051,0x00000006,0x00000dad,0x00000799, ++ 0x00000001,0x00050085,0x00000006,0x00000db0,0x00000dad,0x00000702,0x00050081,0x00000006, ++ 0x00000db1,0x00000dab,0x00000db0,0x00060052,0x0000000c,0x000011d7,0x00000db1,0x0000129d, ++ 0x00000000,0x00050085,0x00000006,0x00000db8,0x00000da8,0x00000c8b,0x00050085,0x00000006, ++ 0x00000dbd,0x00000dad,0x000006fd,0x00050081,0x00000006,0x00000dbe,0x00000db8,0x00000dbd, ++ 0x00060052,0x0000000c,0x000011dd,0x00000dbe,0x000011d7,0x00000001,0x00050085,0x0000000c, ++ 0x00000dc2,0x000011dd,0x0000071c,0x00050051,0x00000006,0x00000dc4,0x00000dc2,0x00000000, ++ 0x00050085,0x00000006,0x00000dc7,0x00000dc4,0x00000dc4,0x00050051,0x00000006,0x00000dc9, ++ 0x00000dc2,0x00000001,0x00050085,0x00000006,0x00000dcc,0x00000dc9,0x00000dc9,0x00050081, ++ 0x00000006,0x00000dcd,0x00000dc7,0x00000dcc,0x0007000c,0x00000006,0x00000dd0,0x00000001, ++ 0x00000025,0x00000dcd,0x00000c55,0x00050085,0x00000006,0x00000dd3,0x0000010d,0x00000dd0, ++ 0x00050081,0x00000006,0x00000dd5,0x00000dd3,0x00000112,0x00050085,0x00000006,0x00000dd8, ++ 0x00000721,0x00000dd0,0x00050081,0x00000006,0x00000dda,0x00000dd8,0x00000112,0x00050085, ++ 0x00000006,0x00000ddd,0x00000dd5,0x00000dd5,0x00050085,0x00000006,0x00000de0,0x00000dda, ++ 0x00000dda,0x00050085,0x00000006,0x00000de3,0x00000123,0x00000ddd,0x00050081,0x00000006, ++ 0x00000de5,0x00000de3,0x00000128,0x00050085,0x00000006,0x00000de8,0x00000de5,0x00000de0, ++ 0x0005008e,0x00000011,0x00000deb,0x00000738,0x00000de8,0x00050081,0x00000011,0x00000ded, ++ 0x00000d89,0x00000deb,0x00050081,0x00000006,0x00000df0,0x00000d8c,0x00000de8,0x0004007f, ++ 0x0000000c,0x000007ab,0x0000062b,0x00050051,0x00000006,0x00000e0c,0x000007ab,0x00000000, ++ 0x00050085,0x00000006,0x00000e0f,0x00000e0c,0x000006fd,0x00050051,0x00000006,0x00000e11, ++ 0x000007ab,0x00000001,0x00050085,0x00000006,0x00000e14,0x00000e11,0x00000702,0x00050081, ++ 0x00000006,0x00000e15,0x00000e0f,0x00000e14,0x00060052,0x0000000c,0x000011ea,0x00000e15, ++ 0x0000129d,0x00000000,0x00050085,0x00000006,0x00000e1c,0x00000e0c,0x00000c8b,0x00050085, ++ 0x00000006,0x00000e21,0x00000e11,0x000006fd,0x00050081,0x00000006,0x00000e22,0x00000e1c, ++ 0x00000e21,0x00060052,0x0000000c,0x000011f0,0x00000e22,0x000011ea,0x00000001,0x00050085, ++ 0x0000000c,0x00000e26,0x000011f0,0x0000071c,0x00050051,0x00000006,0x00000e28,0x00000e26, ++ 0x00000000,0x00050085,0x00000006,0x00000e2b,0x00000e28,0x00000e28,0x00050051,0x00000006, ++ 0x00000e2d,0x00000e26,0x00000001,0x00050085,0x00000006,0x00000e30,0x00000e2d,0x00000e2d, ++ 0x00050081,0x00000006,0x00000e31,0x00000e2b,0x00000e30,0x0007000c,0x00000006,0x00000e34, ++ 0x00000001,0x00000025,0x00000e31,0x00000c55,0x00050085,0x00000006,0x00000e37,0x0000010d, ++ 0x00000e34,0x00050081,0x00000006,0x00000e39,0x00000e37,0x00000112,0x00050085,0x00000006, ++ 0x00000e3c,0x00000721,0x00000e34,0x00050081,0x00000006,0x00000e3e,0x00000e3c,0x00000112, ++ 0x00050085,0x00000006,0x00000e41,0x00000e39,0x00000e39,0x00050085,0x00000006,0x00000e44, ++ 0x00000e3e,0x00000e3e,0x00050085,0x00000006,0x00000e47,0x00000123,0x00000e41,0x00050081, ++ 0x00000006,0x00000e49,0x00000e47,0x00000128,0x00050085,0x00000006,0x00000e4c,0x00000e49, ++ 0x00000e44,0x0005008e,0x00000011,0x00000e4f,0x0000072a,0x00000e4c,0x00050081,0x00000011, ++ 0x00000e51,0x00000ded,0x00000e4f,0x00050081,0x00000006,0x00000e54,0x00000df0,0x00000e4c, ++ 0x00050083,0x0000000c,0x000007bd,0x00000423,0x0000062b,0x00050051,0x00000006,0x000007bf, ++ 0x00000865,0x00000003,0x00050051,0x00000006,0x000007c1,0x0000086f,0x00000003,0x00050051, ++ 0x00000006,0x000007c3,0x00000879,0x00000003,0x00060050,0x00000011,0x000007c4,0x000007bf, ++ 0x000007c1,0x000007c3,0x00050051,0x00000006,0x00000e70,0x000007bd,0x00000000,0x00050085, ++ 0x00000006,0x00000e73,0x00000e70,0x000006fd,0x00050051,0x00000006,0x00000e75,0x000007bd, ++ 0x00000001,0x00050085,0x00000006,0x00000e78,0x00000e75,0x00000702,0x00050081,0x00000006, ++ 0x00000e79,0x00000e73,0x00000e78,0x00060052,0x0000000c,0x000011fd,0x00000e79,0x0000129d, ++ 0x00000000,0x00050085,0x00000006,0x00000e80,0x00000e70,0x00000c8b,0x00050085,0x00000006, ++ 0x00000e85,0x00000e75,0x000006fd,0x00050081,0x00000006,0x00000e86,0x00000e80,0x00000e85, ++ 0x00060052,0x0000000c,0x00001203,0x00000e86,0x000011fd,0x00000001,0x00050085,0x0000000c, ++ 0x00000e8a,0x00001203,0x0000071c,0x00050051,0x00000006,0x00000e8c,0x00000e8a,0x00000000, ++ 0x00050085,0x00000006,0x00000e8f,0x00000e8c,0x00000e8c,0x00050051,0x00000006,0x00000e91, ++ 0x00000e8a,0x00000001,0x00050085,0x00000006,0x00000e94,0x00000e91,0x00000e91,0x00050081, ++ 0x00000006,0x00000e95,0x00000e8f,0x00000e94,0x0007000c,0x00000006,0x00000e98,0x00000001, ++ 0x00000025,0x00000e95,0x00000c55,0x00050085,0x00000006,0x00000e9b,0x0000010d,0x00000e98, ++ 0x00050081,0x00000006,0x00000e9d,0x00000e9b,0x00000112,0x00050085,0x00000006,0x00000ea0, ++ 0x00000721,0x00000e98,0x00050081,0x00000006,0x00000ea2,0x00000ea0,0x00000112,0x00050085, ++ 0x00000006,0x00000ea5,0x00000e9d,0x00000e9d,0x00050085,0x00000006,0x00000ea8,0x00000ea2, ++ 0x00000ea2,0x00050085,0x00000006,0x00000eab,0x00000123,0x00000ea5,0x00050081,0x00000006, ++ 0x00000ead,0x00000eab,0x00000128,0x00050085,0x00000006,0x00000eb0,0x00000ead,0x00000ea8, ++ 0x0005008e,0x00000011,0x00000eb3,0x000007c4,0x00000eb0,0x00050081,0x00000011,0x00000eb5, ++ 0x00000e51,0x00000eb3,0x00050081,0x00000006,0x00000eb8,0x00000e54,0x00000eb0,0x00050083, ++ 0x0000000c,0x000007cf,0x0000043e,0x0000062b,0x00050051,0x00000006,0x00000ed4,0x000007cf, ++ 0x00000000,0x00050085,0x00000006,0x00000ed7,0x00000ed4,0x000006fd,0x00050051,0x00000006, ++ 0x00000ed9,0x000007cf,0x00000001,0x00050085,0x00000006,0x00000edc,0x00000ed9,0x00000702, ++ 0x00050081,0x00000006,0x00000edd,0x00000ed7,0x00000edc,0x00060052,0x0000000c,0x00001210, ++ 0x00000edd,0x0000129d,0x00000000,0x00050085,0x00000006,0x00000ee4,0x00000ed4,0x00000c8b, ++ 0x00050085,0x00000006,0x00000ee9,0x00000ed9,0x000006fd,0x00050081,0x00000006,0x00000eea, ++ 0x00000ee4,0x00000ee9,0x00060052,0x0000000c,0x00001216,0x00000eea,0x00001210,0x00000001, ++ 0x00050085,0x0000000c,0x00000eee,0x00001216,0x0000071c,0x00050051,0x00000006,0x00000ef0, ++ 0x00000eee,0x00000000,0x00050085,0x00000006,0x00000ef3,0x00000ef0,0x00000ef0,0x00050051, ++ 0x00000006,0x00000ef5,0x00000eee,0x00000001,0x00050085,0x00000006,0x00000ef8,0x00000ef5, ++ 0x00000ef5,0x00050081,0x00000006,0x00000ef9,0x00000ef3,0x00000ef8,0x0007000c,0x00000006, ++ 0x00000efc,0x00000001,0x00000025,0x00000ef9,0x00000c55,0x00050085,0x00000006,0x00000eff, ++ 0x0000010d,0x00000efc,0x00050081,0x00000006,0x00000f01,0x00000eff,0x00000112,0x00050085, ++ 0x00000006,0x00000f04,0x00000721,0x00000efc,0x00050081,0x00000006,0x00000f06,0x00000f04, ++ 0x00000112,0x00050085,0x00000006,0x00000f09,0x00000f01,0x00000f01,0x00050085,0x00000006, ++ 0x00000f0c,0x00000f06,0x00000f06,0x00050085,0x00000006,0x00000f0f,0x00000123,0x00000f09, ++ 0x00050081,0x00000006,0x00000f11,0x00000f0f,0x00000128,0x00050085,0x00000006,0x00000f14, ++ 0x00000f11,0x00000f0c,0x0005008e,0x00000011,0x00000f17,0x00000740,0x00000f14,0x00050081, ++ 0x00000011,0x00000f19,0x00000eb5,0x00000f17,0x00050081,0x00000006,0x00000f1c,0x00000eb8, ++ 0x00000f14,0x00050083,0x0000000c,0x000007e1,0x0000045a,0x0000062b,0x00050051,0x00000006, ++ 0x000007e3,0x00000883,0x00000001,0x00050051,0x00000006,0x000007e5,0x0000088d,0x00000001, ++ 0x00050051,0x00000006,0x000007e7,0x00000897,0x00000001,0x00060050,0x00000011,0x000007e8, ++ 0x000007e3,0x000007e5,0x000007e7,0x00050051,0x00000006,0x00000f38,0x000007e1,0x00000000, ++ 0x00050085,0x00000006,0x00000f3b,0x00000f38,0x000006fd,0x00050051,0x00000006,0x00000f3d, ++ 0x000007e1,0x00000001,0x00050085,0x00000006,0x00000f40,0x00000f3d,0x00000702,0x00050081, ++ 0x00000006,0x00000f41,0x00000f3b,0x00000f40,0x00060052,0x0000000c,0x00001223,0x00000f41, ++ 0x0000129d,0x00000000,0x00050085,0x00000006,0x00000f48,0x00000f38,0x00000c8b,0x00050085, ++ 0x00000006,0x00000f4d,0x00000f3d,0x000006fd,0x00050081,0x00000006,0x00000f4e,0x00000f48, ++ 0x00000f4d,0x00060052,0x0000000c,0x00001229,0x00000f4e,0x00001223,0x00000001,0x00050085, ++ 0x0000000c,0x00000f52,0x00001229,0x0000071c,0x00050051,0x00000006,0x00000f54,0x00000f52, ++ 0x00000000,0x00050085,0x00000006,0x00000f57,0x00000f54,0x00000f54,0x00050051,0x00000006, ++ 0x00000f59,0x00000f52,0x00000001,0x00050085,0x00000006,0x00000f5c,0x00000f59,0x00000f59, ++ 0x00050081,0x00000006,0x00000f5d,0x00000f57,0x00000f5c,0x0007000c,0x00000006,0x00000f60, ++ 0x00000001,0x00000025,0x00000f5d,0x00000c55,0x00050085,0x00000006,0x00000f63,0x0000010d, ++ 0x00000f60,0x00050081,0x00000006,0x00000f65,0x00000f63,0x00000112,0x00050085,0x00000006, ++ 0x00000f68,0x00000721,0x00000f60,0x00050081,0x00000006,0x00000f6a,0x00000f68,0x00000112, ++ 0x00050085,0x00000006,0x00000f6d,0x00000f65,0x00000f65,0x00050085,0x00000006,0x00000f70, ++ 0x00000f6a,0x00000f6a,0x00050085,0x00000006,0x00000f73,0x00000123,0x00000f6d,0x00050081, ++ 0x00000006,0x00000f75,0x00000f73,0x00000128,0x00050085,0x00000006,0x00000f78,0x00000f75, ++ 0x00000f70,0x0005008e,0x00000011,0x00000f7b,0x000007e8,0x00000f78,0x00050081,0x00000011, ++ 0x00000f7d,0x00000f19,0x00000f7b,0x00050081,0x00000006,0x00000f80,0x00000f1c,0x00000f78, ++ 0x00050083,0x0000000c,0x000007f3,0x00000475,0x0000062b,0x00050051,0x00000006,0x000007f5, ++ 0x00000883,0x00000002,0x00050051,0x00000006,0x000007f7,0x0000088d,0x00000002,0x00050051, ++ 0x00000006,0x000007f9,0x00000897,0x00000002,0x00060050,0x00000011,0x000007fa,0x000007f5, ++ 0x000007f7,0x000007f9,0x00050051,0x00000006,0x00000f9c,0x000007f3,0x00000000,0x00050085, ++ 0x00000006,0x00000f9f,0x00000f9c,0x000006fd,0x00050051,0x00000006,0x00000fa1,0x000007f3, ++ 0x00000001,0x00050085,0x00000006,0x00000fa4,0x00000fa1,0x00000702,0x00050081,0x00000006, ++ 0x00000fa5,0x00000f9f,0x00000fa4,0x00060052,0x0000000c,0x00001236,0x00000fa5,0x0000129d, ++ 0x00000000,0x00050085,0x00000006,0x00000fac,0x00000f9c,0x00000c8b,0x00050085,0x00000006, ++ 0x00000fb1,0x00000fa1,0x000006fd,0x00050081,0x00000006,0x00000fb2,0x00000fac,0x00000fb1, ++ 0x00060052,0x0000000c,0x0000123c,0x00000fb2,0x00001236,0x00000001,0x00050085,0x0000000c, ++ 0x00000fb6,0x0000123c,0x0000071c,0x00050051,0x00000006,0x00000fb8,0x00000fb6,0x00000000, ++ 0x00050085,0x00000006,0x00000fbb,0x00000fb8,0x00000fb8,0x00050051,0x00000006,0x00000fbd, ++ 0x00000fb6,0x00000001,0x00050085,0x00000006,0x00000fc0,0x00000fbd,0x00000fbd,0x00050081, ++ 0x00000006,0x00000fc1,0x00000fbb,0x00000fc0,0x0007000c,0x00000006,0x00000fc4,0x00000001, ++ 0x00000025,0x00000fc1,0x00000c55,0x00050085,0x00000006,0x00000fc7,0x0000010d,0x00000fc4, ++ 0x00050081,0x00000006,0x00000fc9,0x00000fc7,0x00000112,0x00050085,0x00000006,0x00000fcc, ++ 0x00000721,0x00000fc4,0x00050081,0x00000006,0x00000fce,0x00000fcc,0x00000112,0x00050085, ++ 0x00000006,0x00000fd1,0x00000fc9,0x00000fc9,0x00050085,0x00000006,0x00000fd4,0x00000fce, ++ 0x00000fce,0x00050085,0x00000006,0x00000fd7,0x00000123,0x00000fd1,0x00050081,0x00000006, ++ 0x00000fd9,0x00000fd7,0x00000128,0x00050085,0x00000006,0x00000fdc,0x00000fd9,0x00000fd4, ++ 0x0005008e,0x00000011,0x00000fdf,0x000007fa,0x00000fdc,0x00050081,0x00000011,0x00000fe1, ++ 0x00000f7d,0x00000fdf,0x00050081,0x00000006,0x00000fe4,0x00000f80,0x00000fdc,0x00050083, ++ 0x0000000c,0x00000805,0x00000490,0x0000062b,0x00050051,0x00000006,0x00001000,0x00000805, ++ 0x00000000,0x00050085,0x00000006,0x00001003,0x00001000,0x000006fd,0x00050051,0x00000006, ++ 0x00001005,0x00000805,0x00000001,0x00050085,0x00000006,0x00001008,0x00001005,0x00000702, ++ 0x00050081,0x00000006,0x00001009,0x00001003,0x00001008,0x00060052,0x0000000c,0x00001249, ++ 0x00001009,0x0000129d,0x00000000,0x00050085,0x00000006,0x00001010,0x00001000,0x00000c8b, ++ 0x00050085,0x00000006,0x00001015,0x00001005,0x000006fd,0x00050081,0x00000006,0x00001016, ++ 0x00001010,0x00001015,0x00060052,0x0000000c,0x0000124f,0x00001016,0x00001249,0x00000001, ++ 0x00050085,0x0000000c,0x0000101a,0x0000124f,0x0000071c,0x00050051,0x00000006,0x0000101c, ++ 0x0000101a,0x00000000,0x00050085,0x00000006,0x0000101f,0x0000101c,0x0000101c,0x00050051, ++ 0x00000006,0x00001021,0x0000101a,0x00000001,0x00050085,0x00000006,0x00001024,0x00001021, ++ 0x00001021,0x00050081,0x00000006,0x00001025,0x0000101f,0x00001024,0x0007000c,0x00000006, ++ 0x00001028,0x00000001,0x00000025,0x00001025,0x00000c55,0x00050085,0x00000006,0x0000102b, ++ 0x0000010d,0x00001028,0x00050081,0x00000006,0x0000102d,0x0000102b,0x00000112,0x00050085, ++ 0x00000006,0x00001030,0x00000721,0x00001028,0x00050081,0x00000006,0x00001032,0x00001030, ++ 0x00000112,0x00050085,0x00000006,0x00001035,0x0000102d,0x0000102d,0x00050085,0x00000006, ++ 0x00001038,0x00001032,0x00001032,0x00050085,0x00000006,0x0000103b,0x00000123,0x00001035, ++ 0x00050081,0x00000006,0x0000103d,0x0000103b,0x00000128,0x00050085,0x00000006,0x00001040, ++ 0x0000103d,0x00001038,0x0005008e,0x00000011,0x00001043,0x00000731,0x00001040,0x00050081, ++ 0x00000011,0x00001045,0x00000fe1,0x00001043,0x00050081,0x00000006,0x00001048,0x00000fe4, ++ 0x00001040,0x00050083,0x0000000c,0x00000817,0x000004ab,0x0000062b,0x00050051,0x00000006, ++ 0x00000819,0x000008a1,0x00000002,0x00050051,0x00000006,0x0000081b,0x000008ab,0x00000002, ++ 0x00050051,0x00000006,0x0000081d,0x000008b5,0x00000002,0x00060050,0x00000011,0x0000081e, ++ 0x00000819,0x0000081b,0x0000081d,0x00050051,0x00000006,0x00001064,0x00000817,0x00000000, ++ 0x00050085,0x00000006,0x00001067,0x00001064,0x000006fd,0x00050051,0x00000006,0x00001069, ++ 0x00000817,0x00000001,0x00050085,0x00000006,0x0000106c,0x00001069,0x00000702,0x00050081, ++ 0x00000006,0x0000106d,0x00001067,0x0000106c,0x00060052,0x0000000c,0x0000125c,0x0000106d, ++ 0x0000129d,0x00000000,0x00050085,0x00000006,0x00001074,0x00001064,0x00000c8b,0x00050085, ++ 0x00000006,0x00001079,0x00001069,0x000006fd,0x00050081,0x00000006,0x0000107a,0x00001074, ++ 0x00001079,0x00060052,0x0000000c,0x00001262,0x0000107a,0x0000125c,0x00000001,0x00050085, ++ 0x0000000c,0x0000107e,0x00001262,0x0000071c,0x00050051,0x00000006,0x00001080,0x0000107e, ++ 0x00000000,0x00050085,0x00000006,0x00001083,0x00001080,0x00001080,0x00050051,0x00000006, ++ 0x00001085,0x0000107e,0x00000001,0x00050085,0x00000006,0x00001088,0x00001085,0x00001085, ++ 0x00050081,0x00000006,0x00001089,0x00001083,0x00001088,0x0007000c,0x00000006,0x0000108c, ++ 0x00000001,0x00000025,0x00001089,0x00000c55,0x00050085,0x00000006,0x0000108f,0x0000010d, ++ 0x0000108c,0x00050081,0x00000006,0x00001091,0x0000108f,0x00000112,0x00050085,0x00000006, ++ 0x00001094,0x00000721,0x0000108c,0x00050081,0x00000006,0x00001096,0x00001094,0x00000112, ++ 0x00050085,0x00000006,0x00001099,0x00001091,0x00001091,0x00050085,0x00000006,0x0000109c, ++ 0x00001096,0x00001096,0x00050085,0x00000006,0x0000109f,0x00000123,0x00001099,0x00050081, ++ 0x00000006,0x000010a1,0x0000109f,0x00000128,0x00050085,0x00000006,0x000010a4,0x000010a1, ++ 0x0000109c,0x0005008e,0x00000011,0x000010a7,0x0000081e,0x000010a4,0x00050081,0x00000011, ++ 0x000010a9,0x00001045,0x000010a7,0x00050081,0x00000006,0x000010ac,0x00001048,0x000010a4, ++ 0x00050083,0x0000000c,0x00000829,0x000004c6,0x0000062b,0x00050051,0x00000006,0x0000082b, ++ 0x000008a1,0x00000003,0x00050051,0x00000006,0x0000082d,0x000008ab,0x00000003,0x00050051, ++ 0x00000006,0x0000082f,0x000008b5,0x00000003,0x00060050,0x00000011,0x00000830,0x0000082b, ++ 0x0000082d,0x0000082f,0x00050051,0x00000006,0x000010c8,0x00000829,0x00000000,0x00050085, ++ 0x00000006,0x000010cb,0x000010c8,0x000006fd,0x00050051,0x00000006,0x000010cd,0x00000829, ++ 0x00000001,0x00050085,0x00000006,0x000010d0,0x000010cd,0x00000702,0x00050081,0x00000006, ++ 0x000010d1,0x000010cb,0x000010d0,0x00060052,0x0000000c,0x0000126f,0x000010d1,0x0000129d, ++ 0x00000000,0x00050085,0x00000006,0x000010d8,0x000010c8,0x00000c8b,0x00050085,0x00000006, ++ 0x000010dd,0x000010cd,0x000006fd,0x00050081,0x00000006,0x000010de,0x000010d8,0x000010dd, ++ 0x00060052,0x0000000c,0x00001275,0x000010de,0x0000126f,0x00000001,0x00050085,0x0000000c, ++ 0x000010e2,0x00001275,0x0000071c,0x00050051,0x00000006,0x000010e4,0x000010e2,0x00000000, ++ 0x00050085,0x00000006,0x000010e7,0x000010e4,0x000010e4,0x00050051,0x00000006,0x000010e9, ++ 0x000010e2,0x00000001,0x00050085,0x00000006,0x000010ec,0x000010e9,0x000010e9,0x00050081, ++ 0x00000006,0x000010ed,0x000010e7,0x000010ec,0x0007000c,0x00000006,0x000010f0,0x00000001, ++ 0x00000025,0x000010ed,0x00000c55,0x00050085,0x00000006,0x000010f3,0x0000010d,0x000010f0, ++ 0x00050081,0x00000006,0x000010f5,0x000010f3,0x00000112,0x00050085,0x00000006,0x000010f8, ++ 0x00000721,0x000010f0,0x00050081,0x00000006,0x000010fa,0x000010f8,0x00000112,0x00050085, ++ 0x00000006,0x000010fd,0x000010f5,0x000010f5,0x00050085,0x00000006,0x00001100,0x000010fa, ++ 0x000010fa,0x00050085,0x00000006,0x00001103,0x00000123,0x000010fd,0x00050081,0x00000006, ++ 0x00001105,0x00001103,0x00000128,0x00050085,0x00000006,0x00001108,0x00001105,0x00001100, ++ 0x0005008e,0x00000011,0x0000110b,0x00000830,0x00001108,0x00050081,0x00000011,0x0000110d, ++ 0x000010a9,0x0000110b,0x00050081,0x00000006,0x00001110,0x000010ac,0x00001108,0x00050088, ++ 0x00000006,0x00001125,0x00000093,0x00001110,0x00060050,0x00000011,0x0000112e,0x00001125, ++ 0x00001125,0x00001125,0x00050085,0x00000011,0x00000840,0x0000110d,0x0000112e,0x0007000c, ++ 0x00000011,0x00000841,0x00000001,0x00000028,0x00000741,0x00000840,0x0007000c,0x00000011, ++ 0x00000842,0x00000001,0x00000025,0x0000075f,0x00000841,0x0004003d,0x00000515,0x00000518, ++ 0x00000517,0x0004007c,0x0000051b,0x0000051c,0x000004f0,0x00050051,0x00000006,0x0000051e, ++ 0x00000842,0x00000000,0x00050051,0x00000006,0x0000051f,0x00000842,0x00000001,0x00050051, ++ 0x00000006,0x00000520,0x00000842,0x00000002,0x00070050,0x00000016,0x00000521,0x0000051e, ++ 0x0000051f,0x00000520,0x00000093,0x00040063,0x00000518,0x0000051c,0x00000521,0x000200f9, ++ 0x00000524,0x000200f8,0x00000524,0x000100fd,0x00010038 ++}; ++ ++/* ++#version 460 ++#extension GL_GOOGLE_include_directive: require ++ ++layout(local_size_x=8, local_size_y=8, local_size_z=1) in; ++ ++layout(binding = 0) uniform sampler2D texSampler; ++layout(binding = 1) uniform writeonly image2D outImage; ++ ++layout(push_constant) uniform pushConstants { ++ uvec2 c1; ++ ivec2 extent; ++ ivec4 viewport; ++}; ++ ++#define A_GPU 1 ++#define A_GLSL 1 ++//#include "ffx_a.h" ++#define FSR_RCAS_F 1 ++vec4 FsrRcasLoadF(ivec2 p) { return texelFetch(texSampler, clamp(p, ivec2(0), extent), 0); } ++void FsrRcasInputF(inout float r, inout float g, inout float b) {} ++//#include "ffx_fsr1.h" ++ ++ ++void main() ++{ ++ vec3 color; ++ ++ if (any(lessThan(gl_GlobalInvocationID.xy, uvec2(viewport.xy))) || ++ any(greaterThan(gl_GlobalInvocationID.xy, uvec2(viewport.zw)))) ++ { ++ color = vec3(0.0, 0.0, 0.0); ++ } ++ else ++ { ++ FsrRcasF(color.r, color.g, color.b, uvec2(gl_GlobalInvocationID.xy - viewport.xy), c1.xyxx); ++ } ++ ++ imageStore(outImage, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1.0)); ++} ++*/ ++const uint32_t fsr_rcas_comp_spv[] = { ++ 0x07230203,0x00010000,0x0008000a,0x0000061e,0x00000000,0x00020011,0x00000001,0x00020011, ++ 0x00000038,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e, ++ 0x00000000,0x00000001,0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x00000287, ++ 0x00060010,0x00000004,0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002, ++ 0x000001cc,0x000a0004,0x475f4c47,0x4c474f4f,0x70635f45,0x74735f70,0x5f656c79,0x656e696c, ++ 0x7269645f,0x69746365,0x00006576,0x00080004,0x475f4c47,0x4c474f4f,0x6e695f45,0x64756c63, ++ 0x69645f65,0x74636572,0x00657669,0x00040005,0x00000004,0x6e69616d,0x00000000,0x00050005, ++ 0x0000007b,0x53786574,0x6c706d61,0x00007265,0x00060005,0x00000081,0x68737570,0x736e6f43, ++ 0x746e6174,0x00000073,0x00040006,0x00000081,0x00000000,0x00003163,0x00050006,0x00000081, ++ 0x00000001,0x65747865,0x0000746e,0x00060006,0x00000081,0x00000002,0x77656976,0x74726f70, ++ 0x00000000,0x00030005,0x00000083,0x00000000,0x00080005,0x00000287,0x475f6c67,0x61626f6c, ++ 0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00050005,0x000002c0,0x4974756f,0x6567616d, ++ 0x00000000,0x00040047,0x0000007b,0x00000022,0x00000000,0x00040047,0x0000007b,0x00000021, ++ 0x00000000,0x00050048,0x00000081,0x00000000,0x00000023,0x00000000,0x00050048,0x00000081, ++ 0x00000001,0x00000023,0x00000008,0x00050048,0x00000081,0x00000002,0x00000023,0x00000010, ++ 0x00030047,0x00000081,0x00000002,0x00040047,0x00000287,0x0000000b,0x0000001c,0x00040047, ++ 0x000002c0,0x00000022,0x00000000,0x00040047,0x000002c0,0x00000021,0x00000001,0x00030047, ++ 0x000002c0,0x00000019,0x00040047,0x000002cb,0x0000000b,0x00000019,0x00020013,0x00000002, ++ 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040015,0x0000000c, ++ 0x00000020,0x00000000,0x00040015,0x00000026,0x00000020,0x00000001,0x00040017,0x00000027, ++ 0x00000026,0x00000002,0x00040017,0x00000029,0x00000006,0x00000004,0x00040017,0x00000034, ++ 0x0000000c,0x00000002,0x0004002b,0x00000006,0x00000054,0x3f800000,0x0004002b,0x00000006, ++ 0x0000005c,0x00000000,0x0004002b,0x0000000c,0x00000065,0x7ef19fff,0x0004002b,0x00000006, ++ 0x00000071,0x40000000,0x00090019,0x00000078,0x00000006,0x00000001,0x00000000,0x00000000, ++ 0x00000000,0x00000001,0x00000000,0x0003001b,0x00000079,0x00000078,0x00040020,0x0000007a, ++ 0x00000000,0x00000079,0x0004003b,0x0000007a,0x0000007b,0x00000000,0x0004002b,0x00000026, ++ 0x0000007e,0x00000000,0x0005002c,0x00000027,0x0000007f,0x0000007e,0x0000007e,0x00040017, ++ 0x00000080,0x00000026,0x00000004,0x0005001e,0x00000081,0x00000034,0x00000027,0x00000080, ++ 0x00040020,0x00000082,0x00000009,0x00000081,0x0004003b,0x00000082,0x00000083,0x00000009, ++ 0x0004002b,0x00000026,0x00000084,0x00000001,0x00040020,0x00000085,0x00000009,0x00000027, ++ 0x00040017,0x00000090,0x00000006,0x00000003,0x0004002b,0x00000026,0x00000094,0xffffffff, ++ 0x0005002c,0x00000027,0x00000095,0x0000007e,0x00000094,0x0005002c,0x00000027,0x0000009c, ++ 0x00000094,0x0000007e,0x0005002c,0x00000027,0x000000a8,0x00000084,0x0000007e,0x0005002c, ++ 0x00000027,0x000000af,0x0000007e,0x00000084,0x0004002b,0x0000000c,0x000000b9,0x00000001, ++ 0x0004002b,0x00000006,0x00000154,0x3e800000,0x0004002b,0x00000006,0x000001d3,0xc0800000, ++ 0x0004002b,0x00000006,0x000001d7,0x40800000,0x0004002b,0x00000006,0x0000022e,0xbe400000, ++ 0x00020014,0x00000284,0x00040017,0x00000285,0x0000000c,0x00000003,0x00040020,0x00000286, ++ 0x00000001,0x00000285,0x0004003b,0x00000286,0x00000287,0x00000001,0x0004002b,0x00000026, ++ 0x0000028a,0x00000002,0x00040020,0x0000028b,0x00000009,0x00000080,0x00040017,0x00000290, ++ 0x00000284,0x00000002,0x0006002c,0x00000090,0x000002a2,0x0000005c,0x0000005c,0x0000005c, ++ 0x00040020,0x000002b3,0x00000009,0x00000034,0x00090019,0x000002be,0x00000006,0x00000001, ++ 0x00000000,0x00000000,0x00000000,0x00000002,0x00000000,0x00040020,0x000002bf,0x00000000, ++ 0x000002be,0x0004003b,0x000002bf,0x000002c0,0x00000000,0x0004002b,0x0000000c,0x000002ca, ++ 0x00000008,0x0006002c,0x00000285,0x000002cb,0x000002ca,0x000002ca,0x000000b9,0x00030001, ++ 0x00000090,0x0000061d,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, ++ 0x00000005,0x0004003d,0x00000285,0x00000288,0x00000287,0x0007004f,0x00000034,0x00000289, ++ 0x00000288,0x00000288,0x00000000,0x00000001,0x00050041,0x0000028b,0x0000028c,0x00000083, ++ 0x0000028a,0x0004003d,0x00000080,0x0000028d,0x0000028c,0x0007004f,0x00000027,0x0000028e, ++ 0x0000028d,0x0000028d,0x00000000,0x00000001,0x0004007c,0x00000034,0x0000028f,0x0000028e, ++ 0x000500b0,0x00000290,0x00000291,0x00000289,0x0000028f,0x0004009a,0x00000284,0x00000292, ++ 0x00000291,0x000400a8,0x00000284,0x00000293,0x00000292,0x000300f7,0x00000295,0x00000000, ++ 0x000400fa,0x00000293,0x00000294,0x00000295,0x000200f8,0x00000294,0x0007004f,0x00000027, ++ 0x0000029a,0x0000028d,0x0000028d,0x00000002,0x00000003,0x0004007c,0x00000034,0x0000029b, ++ 0x0000029a,0x000500ac,0x00000290,0x0000029c,0x00000289,0x0000029b,0x0004009a,0x00000284, ++ 0x0000029d,0x0000029c,0x000200f9,0x00000295,0x000200f8,0x00000295,0x000700f5,0x00000284, ++ 0x0000029e,0x00000292,0x00000005,0x0000029d,0x00000294,0x000300f7,0x000002a0,0x00000000, ++ 0x000400fa,0x0000029e,0x0000029f,0x000002a3,0x000200f8,0x0000029f,0x000200f9,0x000002a0, ++ 0x000200f8,0x000002a3,0x00050082,0x00000034,0x000002aa,0x00000289,0x0000028f,0x00050041, ++ 0x000002b3,0x000002b4,0x00000083,0x0000007e,0x0004003d,0x00000034,0x000002b5,0x000002b4, ++ 0x0004007c,0x00000027,0x00000353,0x000002aa,0x00050080,0x00000027,0x00000355,0x00000353, ++ 0x00000095,0x0004003d,0x00000079,0x000004b3,0x0000007b,0x00050041,0x00000085,0x000004b5, ++ 0x00000083,0x00000084,0x0004003d,0x00000027,0x000004b6,0x000004b5,0x0008000c,0x00000027, ++ 0x000004b7,0x00000001,0x0000002d,0x00000355,0x0000007f,0x000004b6,0x00040064,0x00000078, ++ 0x000004b8,0x000004b3,0x0007005f,0x00000029,0x000004b9,0x000004b8,0x000004b7,0x00000002, ++ 0x0000007e,0x00050080,0x00000027,0x00000359,0x00000353,0x0000009c,0x0008000c,0x00000027, ++ 0x000004c0,0x00000001,0x0000002d,0x00000359,0x0000007f,0x000004b6,0x00040064,0x00000078, ++ 0x000004c1,0x000004b3,0x0007005f,0x00000029,0x000004c2,0x000004c1,0x000004c0,0x00000002, ++ 0x0000007e,0x0008000c,0x00000027,0x000004c9,0x00000001,0x0000002d,0x00000353,0x0000007f, ++ 0x000004b6,0x00040064,0x00000078,0x000004ca,0x000004b3,0x0007005f,0x00000029,0x000004cb, ++ 0x000004ca,0x000004c9,0x00000002,0x0000007e,0x00050080,0x00000027,0x00000360,0x00000353, ++ 0x000000a8,0x0008000c,0x00000027,0x000004d2,0x00000001,0x0000002d,0x00000360,0x0000007f, ++ 0x000004b6,0x00040064,0x00000078,0x000004d3,0x000004b3,0x0007005f,0x00000029,0x000004d4, ++ 0x000004d3,0x000004d2,0x00000002,0x0000007e,0x00050080,0x00000027,0x00000364,0x00000353, ++ 0x000000af,0x0008000c,0x00000027,0x000004db,0x00000001,0x0000002d,0x00000364,0x0000007f, ++ 0x000004b6,0x00040064,0x00000078,0x000004dc,0x000004b3,0x0007005f,0x00000029,0x000004dd, ++ 0x000004dc,0x000004db,0x00000002,0x0000007e,0x00050051,0x00000006,0x00000368,0x000004b9, ++ 0x00000000,0x00050051,0x00000006,0x0000036a,0x000004b9,0x00000001,0x00050051,0x00000006, ++ 0x0000036c,0x000004b9,0x00000002,0x00050051,0x00000006,0x0000036e,0x000004c2,0x00000000, ++ 0x00050051,0x00000006,0x00000370,0x000004c2,0x00000001,0x00050051,0x00000006,0x00000372, ++ 0x000004c2,0x00000002,0x00050051,0x00000006,0x00000374,0x000004cb,0x00000000,0x00050051, ++ 0x00000006,0x00000376,0x000004cb,0x00000001,0x00050051,0x00000006,0x00000378,0x000004cb, ++ 0x00000002,0x00050051,0x00000006,0x0000037a,0x000004d4,0x00000000,0x00050051,0x00000006, ++ 0x0000037c,0x000004d4,0x00000001,0x00050051,0x00000006,0x0000037e,0x000004d4,0x00000002, ++ 0x00050051,0x00000006,0x00000380,0x000004dd,0x00000000,0x00050051,0x00000006,0x00000382, ++ 0x000004dd,0x00000001,0x00050051,0x00000006,0x00000384,0x000004dd,0x00000002,0x0007000c, ++ 0x00000006,0x0000055a,0x00000001,0x00000025,0x0000036e,0x0000037a,0x0007000c,0x00000006, ++ 0x0000055b,0x00000001,0x00000025,0x00000368,0x0000055a,0x0007000c,0x00000006,0x00000404, ++ 0x00000001,0x00000025,0x0000055b,0x00000380,0x0007000c,0x00000006,0x00000561,0x00000001, ++ 0x00000025,0x00000370,0x0000037c,0x0007000c,0x00000006,0x00000562,0x00000001,0x00000025, ++ 0x0000036a,0x00000561,0x0007000c,0x00000006,0x0000040a,0x00000001,0x00000025,0x00000562, ++ 0x00000382,0x0007000c,0x00000006,0x00000568,0x00000001,0x00000025,0x00000372,0x0000037e, ++ 0x0007000c,0x00000006,0x00000569,0x00000001,0x00000025,0x0000036c,0x00000568,0x0007000c, ++ 0x00000006,0x00000410,0x00000001,0x00000025,0x00000569,0x00000384,0x0007000c,0x00000006, ++ 0x0000056f,0x00000001,0x00000028,0x0000036e,0x0000037a,0x0007000c,0x00000006,0x00000570, ++ 0x00000001,0x00000028,0x00000368,0x0000056f,0x0007000c,0x00000006,0x00000416,0x00000001, ++ 0x00000028,0x00000570,0x00000380,0x0007000c,0x00000006,0x00000576,0x00000001,0x00000028, ++ 0x00000370,0x0000037c,0x0007000c,0x00000006,0x00000577,0x00000001,0x00000028,0x0000036a, ++ 0x00000576,0x0007000c,0x00000006,0x0000041c,0x00000001,0x00000028,0x00000577,0x00000382, ++ 0x0007000c,0x00000006,0x0000057d,0x00000001,0x00000028,0x00000372,0x0000037e,0x0007000c, ++ 0x00000006,0x0000057e,0x00000001,0x00000028,0x0000036c,0x0000057d,0x0007000c,0x00000006, ++ 0x00000422,0x00000001,0x00000028,0x0000057e,0x00000384,0x00050088,0x00000006,0x00000587, ++ 0x00000154,0x00000416,0x00050085,0x00000006,0x00000428,0x00000404,0x00000587,0x00050088, ++ 0x00000006,0x00000593,0x00000154,0x0000041c,0x00050085,0x00000006,0x0000042e,0x0000040a, ++ 0x00000593,0x00050088,0x00000006,0x0000059f,0x00000154,0x00000422,0x00050085,0x00000006, ++ 0x00000434,0x00000410,0x0000059f,0x00050083,0x00000006,0x00000438,0x00000054,0x00000416, ++ 0x00050085,0x00000006,0x0000043b,0x000001d7,0x00000404,0x00050081,0x00000006,0x0000043e, ++ 0x0000043b,0x000001d3,0x00050088,0x00000006,0x000005ab,0x00000054,0x0000043e,0x00050085, ++ 0x00000006,0x00000440,0x00000438,0x000005ab,0x00050083,0x00000006,0x00000444,0x00000054, ++ 0x0000041c,0x00050085,0x00000006,0x00000447,0x000001d7,0x0000040a,0x00050081,0x00000006, ++ 0x0000044a,0x00000447,0x000001d3,0x00050088,0x00000006,0x000005b7,0x00000054,0x0000044a, ++ 0x00050085,0x00000006,0x0000044c,0x00000444,0x000005b7,0x00050083,0x00000006,0x00000450, ++ 0x00000054,0x00000422,0x00050085,0x00000006,0x00000453,0x000001d7,0x00000410,0x00050081, ++ 0x00000006,0x00000456,0x00000453,0x000001d3,0x00050088,0x00000006,0x000005c3,0x00000054, ++ 0x00000456,0x00050085,0x00000006,0x00000458,0x00000450,0x000005c3,0x0004007f,0x00000006, ++ 0x0000045a,0x00000428,0x0007000c,0x00000006,0x0000045c,0x00000001,0x00000028,0x0000045a, ++ 0x00000440,0x0004007f,0x00000006,0x0000045e,0x0000042e,0x0007000c,0x00000006,0x00000460, ++ 0x00000001,0x00000028,0x0000045e,0x0000044c,0x0004007f,0x00000006,0x00000462,0x00000434, ++ 0x0007000c,0x00000006,0x00000464,0x00000001,0x00000028,0x00000462,0x00000458,0x0007000c, ++ 0x00000006,0x000005cf,0x00000001,0x00000028,0x00000460,0x00000464,0x0007000c,0x00000006, ++ 0x000005d0,0x00000001,0x00000028,0x0000045c,0x000005cf,0x0007000c,0x00000006,0x0000046b, ++ 0x00000001,0x00000025,0x000005d0,0x0000005c,0x0007000c,0x00000006,0x0000046c,0x00000001, ++ 0x00000028,0x0000022e,0x0000046b,0x00050051,0x0000000c,0x0000046e,0x000002b5,0x00000000, ++ 0x0004007c,0x00000006,0x0000046f,0x0000046e,0x00050085,0x00000006,0x00000470,0x0000046c, ++ 0x0000046f,0x00050085,0x00000006,0x00000473,0x000001d7,0x00000470,0x00050081,0x00000006, ++ 0x00000475,0x00000473,0x00000054,0x0004007c,0x0000000c,0x000005e1,0x00000475,0x00050082, ++ 0x0000000c,0x000005e2,0x00000065,0x000005e1,0x0004007c,0x00000006,0x000005e3,0x000005e2, ++ 0x0004007f,0x00000006,0x000005e6,0x000005e3,0x00050085,0x00000006,0x000005e8,0x000005e6, ++ 0x00000475,0x00050081,0x00000006,0x000005ea,0x000005e8,0x00000071,0x00050085,0x00000006, ++ 0x000005eb,0x000005e3,0x000005ea,0x00050081,0x00000006,0x00000611,0x00000368,0x0000036e, ++ 0x00050081,0x00000006,0x00000612,0x00000611,0x00000380,0x00050081,0x00000006,0x00000613, ++ 0x00000612,0x0000037a,0x00050085,0x00000006,0x00000485,0x00000470,0x00000613,0x00050081, ++ 0x00000006,0x00000487,0x00000485,0x00000374,0x00050085,0x00000006,0x00000489,0x00000487, ++ 0x000005eb,0x00050081,0x00000006,0x00000614,0x0000036a,0x00000370,0x00050081,0x00000006, ++ 0x00000615,0x00000614,0x00000382,0x00050081,0x00000006,0x00000616,0x00000615,0x0000037c, ++ 0x00050085,0x00000006,0x00000498,0x00000470,0x00000616,0x00050081,0x00000006,0x0000049a, ++ 0x00000498,0x00000376,0x00050085,0x00000006,0x0000049c,0x0000049a,0x000005eb,0x00050081, ++ 0x00000006,0x00000617,0x0000036c,0x00000372,0x00050081,0x00000006,0x00000618,0x00000617, ++ 0x00000384,0x00050081,0x00000006,0x00000619,0x00000618,0x0000037e,0x00050085,0x00000006, ++ 0x000004ab,0x00000470,0x00000619,0x00050081,0x00000006,0x000004ad,0x000004ab,0x00000378, ++ 0x00050085,0x00000006,0x000004af,0x000004ad,0x000005eb,0x00060052,0x00000090,0x00000609, ++ 0x00000489,0x0000061d,0x00000000,0x00060052,0x00000090,0x0000060b,0x0000049c,0x00000609, ++ 0x00000001,0x00060052,0x00000090,0x0000060d,0x000004af,0x0000060b,0x00000002,0x000200f9, ++ 0x000002a0,0x000200f8,0x000002a0,0x000700f5,0x00000090,0x0000061c,0x000002a2,0x0000029f, ++ 0x0000060d,0x000002a3,0x0004003d,0x000002be,0x000002c1,0x000002c0,0x0004007c,0x00000027, ++ 0x000002c4,0x00000289,0x00050051,0x00000006,0x000002c6,0x0000061c,0x00000000,0x00050051, ++ 0x00000006,0x000002c7,0x0000061c,0x00000001,0x00050051,0x00000006,0x000002c8,0x0000061c, ++ 0x00000002,0x00070050,0x00000029,0x000002c9,0x000002c6,0x000002c7,0x000002c8,0x00000054, ++ 0x00040063,0x000002c1,0x000002c4,0x000002c9,0x000100fd,0x00010038 ++}; ++ ++static void destroy_pipeline(struct wine_device *device, struct fs_comp_pipeline *pipeline) ++{ ++ device->funcs.p_vkDestroyPipeline(device->device, pipeline->pipeline, NULL); ++ pipeline->pipeline = VK_NULL_HANDLE; ++ ++ device->funcs.p_vkDestroyPipelineLayout(device->device, pipeline->pipeline_layout, NULL); ++ pipeline->pipeline_layout = VK_NULL_HANDLE; ++} ++ ++static VkResult create_pipeline(struct wine_device *device, struct wine_swapchain *swapchain, const uint32_t *code, uint32_t code_size, uint32_t push_size, struct fs_comp_pipeline *pipeline) + { +- VkComputePipelineCreateInfo pipelineInfo = {0}; ++#if defined(USE_STRUCT_CONVERSION) ++ VkComputePipelineCreateInfo_host pipelineInfo = {0}; ++ #else ++ VkComputePipelineCreateInfo pipelineInfo = {0}; ++ #endif ++ VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0}; ++ VkShaderModuleCreateInfo shaderInfo = {0}; ++ VkPushConstantRange pushConstants; ++ VkShaderModule shaderModule = 0; + VkResult res; + ++ pipeline->push_size = push_size; ++ ++ pushConstants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ++ pushConstants.offset = 0; ++ pushConstants.size = push_size; ++ ++ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; ++ pipelineLayoutInfo.setLayoutCount = 1; ++ pipelineLayoutInfo.pSetLayouts = &swapchain->descriptor_set_layout; ++ pipelineLayoutInfo.pushConstantRangeCount = 1; ++ pipelineLayoutInfo.pPushConstantRanges = &pushConstants; ++ ++ res = device->funcs.p_vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, NULL, &pipeline->pipeline_layout); ++ if(res != VK_SUCCESS) ++ { ++ ERR("vkCreatePipelineLayout: %d\n", res); ++ goto fail; ++ } ++ ++ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; ++ shaderInfo.codeSize = code_size; ++ shaderInfo.pCode = code; ++ ++ res = device->funcs.p_vkCreateShaderModule(device->device, &shaderInfo, NULL, &shaderModule); ++ if(res != VK_SUCCESS) ++ { ++ ERR("vkCreateShaderModule: %d\n", res); ++ goto fail; ++ } ++ + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + pipelineInfo.stage.module = shaderModule; + pipelineInfo.stage.pName = "main"; +- pipelineInfo.layout = swapchain->pipeline_layout; ++ pipelineInfo.layout = pipeline->pipeline_layout; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + res = device->funcs.p_vkCreateComputePipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, +- NULL, &swapchain->pipeline); +- if (res != VK_SUCCESS) +- { +- ERR("vkCreateComputePipelines: %d\n", res); +- return res; +- } ++ NULL, &pipeline->pipeline); ++ if(res == VK_SUCCESS) ++ goto out; + +- return VK_SUCCESS; ++ ERR("vkCreateComputePipelines: %d\n", res); ++ ++fail: ++ destroy_pipeline(device, pipeline); ++ ++out: ++ device->funcs.p_vkDestroyShaderModule(device->device, shaderModule, NULL); ++ ++ return res; + } + + static VkResult create_descriptor_set(struct wine_device *device, struct wine_swapchain *swapchain, +@@ -1991,7 +2781,7 @@ static VkResult create_descriptor_set(struct wine_device *device, struct wine_sw + userDescriptorImageInfo.sampler = swapchain->sampler; + + realDescriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; +- realDescriptorImageInfo.imageView = hack->blit_view; ++ realDescriptorImageInfo.imageView = swapchain->fsr ? hack->fsr_view : hack->swapchain_view; + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = hack->descriptor_set; +@@ -2011,10 +2801,47 @@ static VkResult create_descriptor_set(struct wine_device *device, struct wine_sw + + device->funcs.p_vkUpdateDescriptorSets(device->device, 2, descriptorWrites, 0, NULL); + ++ if (swapchain->fsr) ++ { ++ res = device->funcs.p_vkAllocateDescriptorSets(device->device, &descriptorAllocInfo, &hack->fsr_set); ++ if (res != VK_SUCCESS) ++ { ++ ERR("vkAllocateDescriptorSets: %d\n", res); ++ return res; ++ } ++ ++ userDescriptorImageInfo.imageView = hack->fsr_view; ++ ++ realDescriptorImageInfo.imageView = hack->swapchain_view; ++ ++ descriptorWrites[0].dstSet = hack->fsr_set; ++ descriptorWrites[1].dstSet = hack->fsr_set; ++ ++ device->funcs.p_vkUpdateDescriptorSets(device->device, 2, descriptorWrites, 0, NULL); ++ } ++ + return VK_SUCCESS; + } + +-static VkResult init_blit_images(struct wine_device *device, struct wine_swapchain *swapchain) ++static VkFormat srgb_to_unorm(VkFormat format) ++{ ++ switch (format) ++ { ++ case VK_FORMAT_R8G8B8A8_SRGB: return VK_FORMAT_R8G8B8A8_UNORM; ++ case VK_FORMAT_B8G8R8A8_SRGB: return VK_FORMAT_B8G8R8A8_UNORM; ++ case VK_FORMAT_R8G8B8_SRGB: return VK_FORMAT_R8G8B8_UNORM; ++ case VK_FORMAT_B8G8R8_SRGB: return VK_FORMAT_B8G8R8_UNORM; ++ case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return VK_FORMAT_A8B8G8R8_UNORM_PACK32; ++ default: return format; ++ } ++} ++ ++static BOOL is_srgb(VkFormat format) ++{ ++ return format != srgb_to_unorm(format); ++} ++ ++static VkResult init_compute_state(struct wine_device *device, struct wine_swapchain *swapchain) + { + VkResult res; + VkSamplerCreateInfo samplerInfo = {0}; +@@ -2022,19 +2849,27 @@ static VkResult init_blit_images(struct wine_device *device, struct wine_swapcha + VkDescriptorPoolCreateInfo poolInfo = {0}; + VkDescriptorSetLayoutBinding layoutBindings[2] = {{0}, {0}}; + VkDescriptorSetLayoutCreateInfo descriptorLayoutInfo = {0}; +- VkPipelineLayoutCreateInfo pipelineLayoutInfo = {0}; +- VkPushConstantRange pushConstants; +- VkShaderModuleCreateInfo shaderInfo = {0}; +- VkShaderModule shaderModule = 0; ++ VkDeviceSize fsrMemTotal = 0, offs; ++ VkImageCreateInfo imageInfo = {0}; ++#if defined(USE_STRUCT_CONVERSION) ++ VkMemoryRequirements_host fsrMemReq; ++ VkMemoryAllocateInfo_host allocInfo = {0}; ++ VkPhysicalDeviceMemoryProperties_host memProperties; ++ VkImageViewCreateInfo_host viewInfo = {0}; ++#else ++ VkMemoryRequirements fsrMemReq; ++ VkMemoryAllocateInfo allocInfo = {0}; ++ VkPhysicalDeviceMemoryProperties memProperties; + VkImageViewCreateInfo viewInfo = {0}; +- uint32_t i; ++#endif ++ uint32_t fsr_memory_type = -1, i; + + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = swapchain->fs_hack_filter; + samplerInfo.minFilter = swapchain->fs_hack_filter; +- samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; +- samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; +- samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; ++ samplerInfo.addressModeU = swapchain->fsr ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; ++ samplerInfo.addressModeV = swapchain->fsr ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; ++ samplerInfo.addressModeW = swapchain->fsr ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; +@@ -2063,6 +2898,13 @@ static VkResult init_blit_images(struct wine_device *device, struct wine_swapcha + poolInfo.pPoolSizes = poolSizes; + poolInfo.maxSets = swapchain->n_images; + ++ if (swapchain->fsr) ++ { ++ poolSizes[0].descriptorCount *= 2; ++ poolSizes[1].descriptorCount *= 2; ++ poolInfo.maxSets *= 2; ++ } ++ + res = device->funcs.p_vkCreateDescriptorPool(device->device, &poolInfo, NULL, &swapchain->descriptor_pool); + if (res != VK_SUCCESS) + { +@@ -2094,40 +2936,131 @@ static VkResult init_blit_images(struct wine_device *device, struct wine_swapcha + goto fail; + } + +- pushConstants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; +- pushConstants.offset = 0; +- pushConstants.size = 4 * sizeof(float); /* 2 * vec2 */ +- +- pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; +- pipelineLayoutInfo.setLayoutCount = 1; +- pipelineLayoutInfo.pSetLayouts = &swapchain->descriptor_set_layout; +- pipelineLayoutInfo.pushConstantRangeCount = 1; +- pipelineLayoutInfo.pPushConstantRanges = &pushConstants; ++ res = create_pipeline(device, swapchain, blit_comp_spv, sizeof(blit_comp_spv), 4 * sizeof(float) /* 2 * vec2 */, &swapchain->blit_pipeline); ++ if(res != VK_SUCCESS) ++ goto fail; + +- res = device->funcs.p_vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, NULL, +- &swapchain->pipeline_layout); +- if (res != VK_SUCCESS) ++ if (swapchain->fsr) + { +- ERR("vkCreatePipelineLayout: %d\n", res); +- goto fail; +- } ++ res = create_pipeline(device, swapchain, fsr_easu_comp_spv, sizeof(fsr_easu_comp_spv), 16 * sizeof(uint32_t) /* 4 * uvec4 */, &swapchain->fsr_easu_pipeline); ++ if (res != VK_SUCCESS) ++ goto fail; ++ res = create_pipeline(device, swapchain, fsr_rcas_comp_spv, sizeof(fsr_rcas_comp_spv), 8 * sizeof(uint32_t) /* uvec4 + ivec4 */, &swapchain->fsr_rcas_pipeline); ++ if (res != VK_SUCCESS) ++ goto fail; + +- shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; +- shaderInfo.codeSize = sizeof(blit_comp_spv); +- shaderInfo.pCode = blit_comp_spv; ++ /* create intermediate fsr images */ ++ for (i = 0; i < swapchain->n_images; ++i) ++ { ++ struct fs_hack_image *hack = &swapchain->fs_hack_images[i]; ++ ++ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; ++ imageInfo.imageType = VK_IMAGE_TYPE_2D; ++ imageInfo.extent.width = swapchain->blit_dst.extent.width; ++ imageInfo.extent.height = swapchain->blit_dst.extent.height; ++ imageInfo.extent.depth = 1; ++ imageInfo.mipLevels = 1; ++ imageInfo.arrayLayers = 1; ++ imageInfo.format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; ++ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ++ imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ++ imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; ++ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; ++ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; ++ res = device->funcs.p_vkCreateImage(device->device, &imageInfo, NULL, &hack->fsr_image); ++ if (res != VK_SUCCESS) ++ { ++ ERR("vkCreateImage failed: %d\n", res); ++ goto fail; ++ } + +- res = device->funcs.p_vkCreateShaderModule(device->device, &shaderInfo, NULL, &shaderModule); +- if (res != VK_SUCCESS) +- { +- ERR("vkCreateShaderModule: %d\n", res); +- goto fail; +- } ++ device->funcs.p_vkGetImageMemoryRequirements(device->device, hack->fsr_image, &fsrMemReq); + +- res = create_pipeline(device, swapchain, shaderModule); +- if (res != VK_SUCCESS) +- goto fail; ++ offs = fsrMemTotal % fsrMemReq.alignment; ++ if(offs) ++ fsrMemTotal += fsrMemReq.alignment - offs; + +- device->funcs.p_vkDestroyShaderModule(device->device, shaderModule, NULL); ++ fsrMemTotal += fsrMemReq.size; ++ } ++ ++ /* allocate backing memory */ ++ device->phys_dev->instance->funcs.p_vkGetPhysicalDeviceMemoryProperties(device->phys_dev->phys_dev, &memProperties); ++ ++ for (i = 0; i < memProperties.memoryTypeCount; i++) ++ { ++ if ((memProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ++ { ++ if (fsrMemReq.memoryTypeBits & (1 << i)) ++ { ++ fsr_memory_type = i; ++ break; ++ } ++ } ++ } ++ ++ if (fsr_memory_type == -1) ++ { ++ ERR("unable to find suitable memory type\n"); ++ res = VK_ERROR_OUT_OF_HOST_MEMORY; ++ goto fail; ++ } ++ ++ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; ++ allocInfo.allocationSize = fsrMemTotal; ++ allocInfo.memoryTypeIndex = fsr_memory_type; ++ ++ res = device->funcs.p_vkAllocateMemory(device->device, &allocInfo, NULL, &swapchain->fsr_image_memory); ++ if (res != VK_SUCCESS) ++ { ++ ERR("vkAllocateMemory: %d\n", res); ++ goto fail; ++ } ++ ++ /* bind backing memory and create imageviews */ ++ fsrMemTotal = 0; ++ for (i = 0; i < swapchain->n_images; ++i) ++ { ++ struct fs_hack_image *hack = &swapchain->fs_hack_images[i]; ++ ++ device->funcs.p_vkGetImageMemoryRequirements(device->device, hack->fsr_image, &fsrMemReq); ++ ++ offs = fsrMemTotal % fsrMemReq.alignment; ++ if(offs) ++ fsrMemTotal += fsrMemReq.alignment - offs; ++ ++ res = device->funcs.p_vkBindImageMemory(device->device, hack->fsr_image, swapchain->fsr_image_memory, fsrMemTotal); ++ if(res != VK_SUCCESS) ++ { ++ ERR("vkBindImageMemory: %d\n", res); ++ goto fail; ++ } ++ ++ fsrMemTotal += fsrMemReq.size; ++ } ++ ++ /* create imageviews */ ++ for (i = 0; i < swapchain->n_images; ++i) ++ { ++ struct fs_hack_image *hack = &swapchain->fs_hack_images[i]; ++ ++ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ++ viewInfo.image = hack->fsr_image; ++ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; ++ viewInfo.format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; ++ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ++ viewInfo.subresourceRange.baseMipLevel = 0; ++ viewInfo.subresourceRange.levelCount = 1; ++ viewInfo.subresourceRange.baseArrayLayer = 0; ++ viewInfo.subresourceRange.layerCount = 1; ++ ++ res = device->funcs.p_vkCreateImageView(device->device, &viewInfo, NULL, &hack->fsr_view); ++ if(res != VK_SUCCESS) ++ { ++ ERR("vkCreateImageView(blit): %d\n", res); ++ goto fail; ++ } ++ } ++ } + + /* create imageviews */ + for (i = 0; i < swapchain->n_images; ++i) +@@ -2137,14 +3070,14 @@ static VkResult init_blit_images(struct wine_device *device, struct wine_swapcha + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = hack->swapchain_image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; +- viewInfo.format = VK_FORMAT_B8G8R8A8_UNORM; ++ viewInfo.format = srgb_to_unorm(swapchain->format); + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + +- res = device->funcs.p_vkCreateImageView(device->device, &viewInfo, NULL, &hack->blit_view); ++ res = device->funcs.p_vkCreateImageView(device->device, &viewInfo, NULL, &hack->swapchain_view); + if (res != VK_SUCCESS) + { + ERR("vkCreateImageView(blit): %d\n", res); +@@ -2163,17 +3096,19 @@ fail: + { + struct fs_hack_image *hack = &swapchain->fs_hack_images[i]; + +- device->funcs.p_vkDestroyImageView(device->device, hack->blit_view, NULL); +- hack->blit_view = VK_NULL_HANDLE; +- } ++ device->funcs.p_vkDestroyImageView(device->device, hack->fsr_view, NULL); ++ hack->fsr_view = VK_NULL_HANDLE; + +- device->funcs.p_vkDestroyShaderModule(device->device, shaderModule, NULL); ++ device->funcs.p_vkDestroyImageView(device->device, hack->swapchain_view, NULL); ++ hack->swapchain_view = VK_NULL_HANDLE; + +- device->funcs.p_vkDestroyPipeline(device->device, swapchain->pipeline, NULL); +- swapchain->pipeline = VK_NULL_HANDLE; ++ device->funcs.p_vkDestroyImage(device->device, hack->fsr_image, NULL); ++ hack->fsr_image = VK_NULL_HANDLE; ++ } + +- device->funcs.p_vkDestroyPipelineLayout(device->device, swapchain->pipeline_layout, NULL); +- swapchain->pipeline_layout = VK_NULL_HANDLE; ++ destroy_pipeline(device, &swapchain->blit_pipeline); ++ destroy_pipeline(device, &swapchain->fsr_easu_pipeline); ++ destroy_pipeline(device, &swapchain->fsr_rcas_pipeline); + + device->funcs.p_vkDestroyDescriptorSetLayout(device->device, swapchain->descriptor_set_layout, NULL); + swapchain->descriptor_set_layout = VK_NULL_HANDLE; +@@ -2181,6 +3116,9 @@ fail: + device->funcs.p_vkDestroyDescriptorPool(device->device, swapchain->descriptor_pool, NULL); + swapchain->descriptor_pool = VK_NULL_HANDLE; + ++ device->funcs.p_vkFreeMemory(device->device, swapchain->fsr_image_memory, NULL); ++ swapchain->fsr_image_memory = VK_NULL_HANDLE; ++ + device->funcs.p_vkDestroySampler(device->device, swapchain->sampler, NULL); + swapchain->sampler = VK_NULL_HANDLE; + +@@ -2191,8 +3129,10 @@ static void destroy_fs_hack_image(struct wine_device *device, struct wine_swapch + struct fs_hack_image *hack) + { + device->funcs.p_vkDestroyImageView(device->device, hack->user_view, NULL); +- device->funcs.p_vkDestroyImageView(device->device, hack->blit_view, NULL); ++ device->funcs.p_vkDestroyImageView(device->device, hack->swapchain_view, NULL); ++ device->funcs.p_vkDestroyImageView(device->device, hack->fsr_view, NULL); + device->funcs.p_vkDestroyImage(device->device, hack->user_image, NULL); ++ device->funcs.p_vkDestroyImage(device->device, hack->fsr_image, NULL); + if (hack->cmd) + device->funcs.p_vkFreeCommandBuffers(device->device, swapchain->cmd_pools[hack->cmd_queue_idx], + 1, &hack->cmd); +@@ -2264,10 +3204,11 @@ static VkResult init_fs_hack_images(struct wine_device *device, struct wine_swap + imageInfo.queueFamilyIndexCount = createinfo->queueFamilyIndexCount; + imageInfo.pQueueFamilyIndices = createinfo->pQueueFamilyIndices; + ++ if (is_srgb(createinfo->imageFormat)) ++ imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; ++ + if (createinfo->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) +- imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; +- else if (createinfo->imageFormat != VK_FORMAT_B8G8R8A8_SRGB) +- imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; ++ imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; + + res = device->funcs.p_vkCreateImage(device->device, &imageInfo, NULL, &hack->user_image); + if (res != VK_SUCCESS) +@@ -2344,7 +3285,7 @@ static VkResult init_fs_hack_images(struct wine_device *device, struct wine_swap + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = swapchain->fs_hack_images[i].user_image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; +- viewInfo.format = VK_FORMAT_B8G8R8A8_SRGB; ++ viewInfo.format = srgb_to_unorm(createinfo->imageFormat); + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; +@@ -2395,7 +3336,7 @@ VkResult wine_vkCreateSwapchainKHR(VkDevice device_handle, const VkSwapchainCrea + + if (vk_funcs->query_fs_hack && + vk_funcs->query_fs_hack(create_info_host.surface, &object->real_extent, &user_sz, +- &object->blit_dst, &object->fs_hack_filter) && ++ &object->blit_dst, &object->fs_hack_filter, &object->fsr, &object->sharpness) && + create_info_host.imageExtent.width == user_sz.width && + create_info_host.imageExtent.height == user_sz.height) + { +@@ -2420,8 +3361,16 @@ VkResult wine_vkCreateSwapchainKHR(VkDevice device_handle, const VkSwapchainCrea + FIXME("Swapchain does not support required VK_IMAGE_USAGE_STORAGE_BIT\n"); + + create_info_host.imageExtent = object->real_extent; +- create_info_host.imageFormat = VK_FORMAT_B8G8R8A8_UNORM; +- create_info_host.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT; ++ create_info_host.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; ++ create_info_host.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT; ++ ++ object->format = create_info_host.imageFormat; ++ ++ if (object->fsr) { ++ object->format = srgb_to_unorm(object->format); ++ create_info_host.imageFormat = srgb_to_unorm(create_info_host.imageFormat); ++ create_info_host.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; /* XXX: check if supported by surface */ ++ } + + if (info->imageFormat != VK_FORMAT_B8G8R8A8_UNORM && info->imageFormat != VK_FORMAT_B8G8R8A8_SRGB) + FIXME("swapchain image format is not BGRA8 UNORM/SRGB. Things may go badly. %d\n", create_info_host.imageFormat); +@@ -2451,7 +3400,7 @@ VkResult wine_vkCreateSwapchainKHR(VkDevice device_handle, const VkSwapchainCrea + return res; + } + +- res = init_blit_images(device, object); ++ res = init_compute_state(device, object); + if (res != VK_SUCCESS) + { + ERR("creating blit images failed: %d\n", res); +@@ -3053,7 +4002,7 @@ VkResult wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice handle, + adjust_max_image_count(phys_dev, capabilities); + + if (res == VK_SUCCESS && vk_funcs->query_fs_hack && +- vk_funcs->query_fs_hack(surface->driver_surface, NULL, &user_res, NULL, NULL)) ++ vk_funcs->query_fs_hack(surface->driver_surface, NULL, &user_res, NULL, NULL, NULL, NULL)) + { + capabilities->currentExtent = user_res; + capabilities->minImageExtent = user_res; +@@ -3083,7 +4032,7 @@ VkResult wine_vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice handle + adjust_max_image_count(phys_dev, &capabilities->surfaceCapabilities); + + if (res == VK_SUCCESS && vk_funcs->query_fs_hack && +- vk_funcs->query_fs_hack(wine_surface_from_handle(surface_info->surface)->driver_surface, NULL, &user_res, NULL, NULL)) ++ vk_funcs->query_fs_hack(wine_surface_from_handle(surface_info->surface)->driver_surface, NULL, &user_res, NULL, NULL, NULL, NULL)) + { + capabilities->surfaceCapabilities.currentExtent = user_res; + capabilities->surfaceCapabilities.minImageExtent = user_res; +@@ -3273,12 +4222,14 @@ void wine_vkDestroySwapchainKHR(VkDevice device_handle, VkSwapchainKHR handle, c + if (swapchain->cmd_pools[i]) + device->funcs.p_vkDestroyCommandPool(device->device, swapchain->cmd_pools[i], NULL); + +- device->funcs.p_vkDestroyPipeline(device->device, swapchain->pipeline, NULL); +- device->funcs.p_vkDestroyPipelineLayout(device->device, swapchain->pipeline_layout, NULL); ++ destroy_pipeline(device, &swapchain->blit_pipeline); ++ destroy_pipeline(device, &swapchain->fsr_easu_pipeline); ++ destroy_pipeline(device, &swapchain->fsr_rcas_pipeline); + device->funcs.p_vkDestroyDescriptorSetLayout(device->device, swapchain->descriptor_set_layout, NULL); + device->funcs.p_vkDestroyDescriptorPool(device->device, swapchain->descriptor_pool, NULL); + device->funcs.p_vkDestroySampler(device->device, swapchain->sampler, NULL); + device->funcs.p_vkFreeMemory(device->device, swapchain->user_image_memory, NULL); ++ device->funcs.p_vkFreeMemory(device->device, swapchain->fsr_image_memory, NULL); + free(swapchain->cmd_pools); + free(swapchain->fs_hack_images); + } +@@ -3345,16 +4296,55 @@ static VkCommandBuffer create_hack_cmd(struct wine_queue *queue, struct wine_swa + return cmd; + } + ++static void bind_pipeline(struct wine_device *device, VkCommandBuffer cmd, struct fs_comp_pipeline *pipeline, VkDescriptorSet set, void *push_data) ++{ ++ device->funcs.p_vkCmdBindPipeline(cmd, ++ VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); ++ ++ device->funcs.p_vkCmdBindDescriptorSets(cmd, ++ VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline_layout, ++ 0, 1, &set, 0, NULL); ++ ++ device->funcs.p_vkCmdPushConstants(cmd, ++ pipeline->pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, ++ 0, pipeline->push_size, push_data); ++} ++ ++#if defined(USE_STRUCT_CONVERSION) ++static void init_barrier(VkImageMemoryBarrier_host *barrier) ++#else ++static void init_barrier(VkImageMemoryBarrier *barrier) ++#endif ++{ ++ barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; ++ barrier->pNext = NULL; ++ barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; ++ barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; ++ barrier->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ++ barrier->subresourceRange.baseMipLevel = 0; ++ barrier->subresourceRange.levelCount = 1; ++ barrier->subresourceRange.baseArrayLayer = 0; ++ barrier->subresourceRange.layerCount = 1; ++} ++ + static VkResult record_compute_cmd(struct wine_device *device, struct wine_swapchain *swapchain, + struct fs_hack_image *hack) + { +- VkResult result; +- VkImageMemoryBarrier barriers[3] = {{0}}; ++#if defined(USE_STRUCT_CONVERSION) ++ VkImageMemoryBarrier_host barriers[2] = {{0}}; ++ VkCommandBufferBeginInfo_host beginInfo = {0}; ++#else ++ VkImageMemoryBarrier barriers[2] = {{0}}; + VkCommandBufferBeginInfo beginInfo = {0}; ++#endif + float constants[4]; ++ VkResult result; + + TRACE("recording compute command\n"); + ++ init_barrier(&barriers[0]); ++ init_barrier(&barriers[1]); ++ + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + +@@ -3362,33 +4352,17 @@ static VkResult record_compute_cmd(struct wine_device *device, struct wine_swapc + + /* for the cs we run... */ + /* transition user image from PRESENT_SRC to SHADER_READ */ +- barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; +- barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +- barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[0].image = hack->user_image; +- barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +- barriers[0].subresourceRange.baseMipLevel = 0; +- barriers[0].subresourceRange.levelCount = 1; +- barriers[0].subresourceRange.baseArrayLayer = 0; +- barriers[0].subresourceRange.layerCount = 1; + barriers[0].srcAccessMask = 0; + barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + /* storage image... */ + /* transition swapchain image from whatever to GENERAL */ +- barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barriers[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; +- barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +- barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[1].image = hack->swapchain_image; +- barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +- barriers[1].subresourceRange.baseMipLevel = 0; +- barriers[1].subresourceRange.levelCount = 1; +- barriers[1].subresourceRange.baseArrayLayer = 0; +- barriers[1].subresourceRange.layerCount = 1; + barriers[1].srcAccessMask = 0; + barriers[1].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + +@@ -3396,10 +4370,6 @@ static VkResult record_compute_cmd(struct wine_device *device, struct wine_swapc + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 2, barriers); + + /* perform blit shader */ +- device->funcs.p_vkCmdBindPipeline(hack->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, swapchain->pipeline); +- +- device->funcs.p_vkCmdBindDescriptorSets(hack->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, +- swapchain->pipeline_layout, 0, 1, &hack->descriptor_set, 0, NULL); + + /* vec2: blit dst offset in real coords */ + constants[0] = swapchain->blit_dst.offset.x; +@@ -3412,40 +4382,24 @@ static VkResult record_compute_cmd(struct wine_device *device, struct wine_swapc + /* vec2: blit dst extents in real coords */ + constants[2] = swapchain->blit_dst.extent.width; + constants[3] = swapchain->blit_dst.extent.height; +- device->funcs.p_vkCmdPushConstants(hack->cmd, swapchain->pipeline_layout, +- VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(constants), constants); ++ ++ bind_pipeline(device, hack->cmd, &swapchain->blit_pipeline, hack->descriptor_set, constants); + + /* local sizes in shader are 8 */ + device->funcs.p_vkCmdDispatch(hack->cmd, ceil(swapchain->real_extent.width / 8.), + ceil(swapchain->real_extent.height / 8.), 1); + + /* transition user image from SHADER_READ back to PRESENT_SRC */ +- barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barriers[0].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +- barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +- barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[0].image = hack->user_image; +- barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +- barriers[0].subresourceRange.baseMipLevel = 0; +- barriers[0].subresourceRange.levelCount = 1; +- barriers[0].subresourceRange.baseArrayLayer = 0; +- barriers[0].subresourceRange.layerCount = 1; + barriers[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[0].dstAccessMask = 0; + + /* transition swapchain image from GENERAL to PRESENT_SRC */ +- barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barriers[1].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +- barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; +- barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[1].image = hack->swapchain_image; +- barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +- barriers[1].subresourceRange.baseMipLevel = 0; +- barriers[1].subresourceRange.levelCount = 1; +- barriers[1].subresourceRange.baseArrayLayer = 0; +- barriers[1].subresourceRange.layerCount = 1; + barriers[1].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barriers[1].dstAccessMask = 0; + +@@ -3462,6 +4416,291 @@ static VkResult record_compute_cmd(struct wine_device *device, struct wine_swapc + return VK_SUCCESS; + } + ++static VkResult record_graphics_cmd(struct wine_device *device, struct wine_swapchain *swapchain, struct fs_hack_image *hack) ++{ ++ VkResult result; ++ VkImageBlit blitregion = {0}; ++ VkImageSubresourceRange range = {0}; ++ VkClearColorValue black = {{0.f, 0.f, 0.f}}; ++#if defined(USE_STRUCT_CONVERSION) ++ VkImageMemoryBarrier_host barriers[2] = {{0}}; ++ VkCommandBufferBeginInfo_host beginInfo = {0}; ++#else ++ VkImageMemoryBarrier barriers[2] = {{0}}; ++ VkCommandBufferBeginInfo beginInfo = {0}; ++#endif ++ ++ TRACE("recording graphics command\n"); ++ ++ init_barrier(&barriers[0]); ++ init_barrier(&barriers[1]); ++ ++ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; ++ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; ++ ++ device->funcs.p_vkBeginCommandBuffer(hack->cmd, &beginInfo); ++ ++ /* transition real image from whatever to TRANSFER_DST_OPTIMAL */ ++ barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; ++ barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; ++ barriers[0].image = hack->swapchain_image; ++ barriers[0].srcAccessMask = 0; ++ barriers[0].dstAccessMask = 0; ++ ++ /* transition user image from PRESENT_SRC to TRANSFER_SRC_OPTIMAL */ ++ barriers[1].oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ++ barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; ++ barriers[1].image = hack->user_image; ++ barriers[1].srcAccessMask = 0; ++ barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; ++ ++ device->funcs.p_vkCmdPipelineBarrier( ++ hack->cmd, ++ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, ++ VK_PIPELINE_STAGE_TRANSFER_BIT, ++ 0, ++ 0, NULL, ++ 0, NULL, ++ 2, barriers ++ ); ++ ++ /* clear the image */ ++ range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ++ range.baseMipLevel = 0; ++ range.levelCount = 1; ++ range.baseArrayLayer = 0; ++ range.layerCount = 1; ++ ++ device->funcs.p_vkCmdClearColorImage( ++ hack->cmd, hack->swapchain_image, ++ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ++ &black, 1, &range); ++ ++ /* perform blit */ ++ blitregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ++ blitregion.srcSubresource.layerCount = 1; ++ blitregion.srcOffsets[0].x = 0; ++ blitregion.srcOffsets[0].y = 0; ++ blitregion.srcOffsets[0].z = 0; ++ blitregion.srcOffsets[1].x = swapchain->user_extent.width; ++ blitregion.srcOffsets[1].y = swapchain->user_extent.height; ++ blitregion.srcOffsets[1].z = 1; ++ blitregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ++ blitregion.dstSubresource.layerCount = 1; ++ blitregion.dstOffsets[0].x = 0; //swapchain->blit_dst.offset.x; ++ blitregion.dstOffsets[0].y = 0; //swapchain->blit_dst.offset.y; ++ blitregion.dstOffsets[0].z = 0; ++ blitregion.dstOffsets[1].x = swapchain->blit_dst.offset.x + swapchain->blit_dst.extent.width; ++ blitregion.dstOffsets[1].y = swapchain->blit_dst.offset.y + swapchain->blit_dst.extent.height; ++ blitregion.dstOffsets[1].z = 1; ++ ++ device->funcs.p_vkCmdBlitImage(hack->cmd, ++ hack->user_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, ++ hack->swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ++ 1, &blitregion, swapchain->fs_hack_filter); ++ ++ /* transition real image from TRANSFER_DST to PRESENT_SRC */ ++ barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; ++ barriers[0].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ++ barriers[0].image = hack->swapchain_image; ++ barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; ++ barriers[0].dstAccessMask = 0; ++ ++ /* transition user image from TRANSFER_SRC_OPTIMAL to back to PRESENT_SRC */ ++ barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; ++ barriers[1].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ++ barriers[1].image = hack->user_image; ++ barriers[1].srcAccessMask = 0; ++ barriers[1].dstAccessMask = 0; ++ ++ device->funcs.p_vkCmdPipelineBarrier( ++ hack->cmd, ++ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ++ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ++ 0, ++ 0, NULL, ++ 0, NULL, ++ 2, barriers ++ ); ++ ++ result = device->funcs.p_vkEndCommandBuffer(hack->cmd); ++ if(result != VK_SUCCESS){ ++ ERR("vkEndCommandBuffer: %d\n", result); ++ return result; ++ } ++ ++ return VK_SUCCESS; ++} ++ ++static VkResult record_fsr_cmd(struct wine_device *device, struct wine_swapchain *swapchain, struct fs_hack_image *hack) ++{ ++#if defined(USE_STRUCT_CONVERSION) ++ VkImageMemoryBarrier_host barriers[3] = {{0}}; ++ VkCommandBufferBeginInfo_host beginInfo = {0}; ++#else ++ VkImageMemoryBarrier barriers[3] = {{0}}; ++ VkCommandBufferBeginInfo beginInfo = {0}; ++#endif ++ union ++ { ++ uint32_t uint[16]; ++ float fp[16]; ++ } c; ++ VkResult result; ++ ++ TRACE("recording compute command\n"); ++ ++ init_barrier(&barriers[0]); ++ init_barrier(&barriers[1]); ++ init_barrier(&barriers[2]); ++ ++ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; ++ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; ++ ++ device->funcs.p_vkBeginCommandBuffer(hack->cmd, &beginInfo); ++ ++ /* 1st pass (easu) */ ++ /* transition user image from PRESENT_SRC to SHADER_READ */ ++ barriers[0].oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ++ barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++ barriers[0].image = hack->user_image; ++ barriers[0].srcAccessMask = 0; ++ barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; ++ ++ /* storage image... */ ++ /* transition fsr image from whatever to GENERAL */ ++ barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; ++ barriers[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; ++ barriers[1].image = hack->swapchain_image; ++ barriers[1].srcAccessMask = 0; ++ barriers[1].dstAccessMask = 0; ++ ++ device->funcs.p_vkCmdPipelineBarrier( ++ hack->cmd, ++ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, ++ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ++ 0, ++ 0, NULL, ++ 0, NULL, ++ 2, barriers ++ ); ++ ++ /* perform easu shader */ ++ ++ c.fp[0] = swapchain->user_extent.width * (1.0f / swapchain->blit_dst.extent.width); ++ c.fp[1] = swapchain->user_extent.height * (1.0f / swapchain->blit_dst.extent.height); ++ c.fp[2] = 0.5f * c.fp[0] - 0.5f; ++ c.fp[3] = 0.5f * c.fp[1] - 0.5f; ++ // Viewport pixel position to normalized image space. ++ // This is used to get upper-left of 'F' tap. ++ c.fp[4] = 1.0f / swapchain->user_extent.width; ++ c.fp[5] = 1.0f / swapchain->user_extent.height; ++ // Centers of gather4, first offset from upper-left of 'F'. ++ // +---+---+ ++ // | | | ++ // +--(0)--+ ++ // | b | c | ++ // +---F---+---+---+ ++ // | e | f | g | h | ++ // +--(1)--+--(2)--+ ++ // | i | j | k | l | ++ // +---+---+---+---+ ++ // | n | o | ++ // +--(3)--+ ++ // | | | ++ // +---+---+ ++ c.fp[6] = 1.0f * c.fp[4]; ++ c.fp[7] = -1.0f * c.fp[5]; ++ // These are from (0) instead of 'F'. ++ c.fp[8] = -1.0f * c.fp[4]; ++ c.fp[9] = 2.0f * c.fp[5]; ++ c.fp[10] = 1.0f * c.fp[4]; ++ c.fp[11] = 2.0f * c.fp[5]; ++ c.fp[12] = 0.0f * c.fp[4]; ++ c.fp[13] = 4.0f * c.fp[5]; ++ c.uint[14] = swapchain->blit_dst.extent.width; ++ c.uint[15] = swapchain->blit_dst.extent.height; ++ ++ bind_pipeline(device, hack->cmd, &swapchain->fsr_easu_pipeline, hack->descriptor_set, c.uint); ++ ++ /* local sizes in shader are 8 */ ++ device->funcs.p_vkCmdDispatch(hack->cmd, ceil(swapchain->blit_dst.extent.width / 8.), ++ ceil(swapchain->blit_dst.extent.height / 8.), 1); ++ ++ /* transition user image from SHADER_READ back to PRESENT_SRC */ ++ barriers[0].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++ barriers[0].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ++ barriers[0].image = hack->user_image; ++ barriers[0].srcAccessMask = 0; ++ barriers[0].dstAccessMask = 0; ++ ++ /* transition fsr image from GENERAL to SHADER_READ */ ++ barriers[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL; ++ barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++ barriers[1].image = hack->swapchain_image; ++ barriers[1].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; ++ barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; ++ ++ /* transition swapchain image from whatever to GENERAL */ ++ barriers[2].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; ++ barriers[2].newLayout = VK_IMAGE_LAYOUT_GENERAL; ++ barriers[2].image = hack->swapchain_image; ++ barriers[2].srcAccessMask = 0; ++ barriers[2].dstAccessMask = 0; ++ ++ device->funcs.p_vkCmdPipelineBarrier( ++ hack->cmd, ++ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ++ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ++ 0, ++ 0, NULL, ++ 0, NULL, ++ 3, barriers ++ ); ++ ++ /* 2nd pass (rcas) */ ++ ++ c.fp[0] = exp2f(-swapchain->sharpness); ++ c.uint[2] = swapchain->blit_dst.extent.width; ++ c.uint[3] = swapchain->blit_dst.extent.height; ++ c.uint[4] = swapchain->blit_dst.offset.x; ++ c.uint[5] = swapchain->blit_dst.offset.y; ++ c.uint[6] = swapchain->blit_dst.offset.x + swapchain->blit_dst.extent.width; ++ c.uint[7] = swapchain->blit_dst.offset.y + swapchain->blit_dst.extent.height; ++ ++ bind_pipeline(device, hack->cmd, &swapchain->fsr_rcas_pipeline, hack->fsr_set, c.uint); ++ ++ /* local sizes in shader are 8 */ ++ device->funcs.p_vkCmdDispatch(hack->cmd, ceil(swapchain->real_extent.width / 8.), ++ ceil(swapchain->real_extent.height / 8.), 1); ++ ++ /* transition swapchain image from GENERAL to PRESENT_SRC */ ++ barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL; ++ barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ++ barriers[0].image = hack->swapchain_image; ++ barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; ++ barriers[0].dstAccessMask = 0; ++ ++ device->funcs.p_vkCmdPipelineBarrier( ++ hack->cmd, ++ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, ++ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ++ 0, ++ 0, NULL, ++ 0, NULL, ++ 1, barriers ++ ); ++ ++ result = device->funcs.p_vkEndCommandBuffer(hack->cmd); ++ if (result != VK_SUCCESS) ++ { ++ ERR("vkEndCommandBuffer: %d\n", result); ++ return result; ++ } ++ ++ return VK_SUCCESS; ++} ++ + VkResult fshack_vk_queue_present(VkQueue queue_handle, const VkPresentInfoKHR *pPresentInfo) + { + struct wine_queue *queue = wine_queue_from_handle(queue_handle); +@@ -3509,12 +4748,26 @@ VkResult fshack_vk_queue_present(VkQueue queue_handle, const VkPresentInfoKHR *p + return VK_ERROR_DEVICE_LOST; + } + +- if (queue->device->queue_props[queue_idx].queueFlags & VK_QUEUE_COMPUTE_BIT) /* TODO */ +- res = record_compute_cmd(queue->device, swapchain, hack); ++ if (swapchain->fsr) ++ { ++ if(queue->device->queue_props[queue_idx].queueFlags & VK_QUEUE_COMPUTE_BIT) ++ res = record_fsr_cmd(queue->device, swapchain, hack); ++ else ++ { ++ ERR("Present queue is not a compute queue!\n"); ++ res = VK_ERROR_DEVICE_LOST; ++ } ++ } + else + { +- ERR("Present queue does not support compute!\n"); +- res = VK_ERROR_DEVICE_LOST; ++ if(queue->device->queue_props[queue_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT) ++ res = record_graphics_cmd(queue->device, swapchain, hack); ++ else if(queue->device->queue_props[queue_idx].queueFlags & VK_QUEUE_COMPUTE_BIT && !is_srgb(swapchain->format)) ++ res = record_compute_cmd(queue->device, swapchain, hack); ++ else{ ++ ERR("Present queue is neither graphics nor compute queue with unorm format!\n"); ++ res = VK_ERROR_DEVICE_LOST; ++ } + } + + if (res != VK_SUCCESS) +diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h +index 864fc392c2c..b09e1487f76 100644 +--- a/dlls/winevulkan/vulkan_private.h ++++ b/dlls/winevulkan/vulkan_private.h +@@ -80,10 +80,18 @@ struct fs_hack_image + uint32_t cmd_queue_idx; + VkCommandBuffer cmd; + VkImage swapchain_image; ++ VkImage fsr_image; + VkImage user_image; + VkSemaphore blit_finished; +- VkImageView user_view, blit_view; +- VkDescriptorSet descriptor_set; ++ VkImageView user_view, swapchain_view, fsr_view; ++ VkDescriptorSet descriptor_set, fsr_set; ++}; ++ ++struct fs_comp_pipeline ++{ ++ VkPipelineLayout pipeline_layout; ++ VkPipeline pipeline; ++ uint32_t push_size; + }; + + struct wine_swapchain +@@ -97,15 +105,20 @@ struct wine_swapchain + VkExtent2D real_extent; + VkRect2D blit_dst; + VkCommandPool *cmd_pools; /* VkCommandPool[device->queue_count] */ +- VkDeviceMemory user_image_memory; ++ VkDeviceMemory user_image_memory, fsr_image_memory; + uint32_t n_images; + struct fs_hack_image *fs_hack_images; /* struct fs_hack_image[n_images] */ + VkFilter fs_hack_filter; + VkSampler sampler; + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout descriptor_set_layout; +- VkPipelineLayout pipeline_layout; +- VkPipeline pipeline; ++ VkFormat format; ++ BOOL fsr; ++ float sharpness; ++ ++ struct fs_comp_pipeline blit_pipeline; ++ struct fs_comp_pipeline fsr_easu_pipeline; ++ struct fs_comp_pipeline fsr_rcas_pipeline; + + struct wine_vk_mapping mapping; + }; +diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c +index 74a87007847..f2162d2d843 100644 +--- a/dlls/winex11.drv/fs.c ++++ b/dlls/winex11.drv/fs.c +@@ -42,12 +42,12 @@ static struct x11drv_settings_handler real_settings_handler; + static BOOL initialized; + + /* A table of resolutions some games expect but host system may not report */ +-static const struct ++struct fs_monitor_size + { + SIZE size; + BOOL additional; +-} +-fs_monitor_sizes[] = ++}; ++static struct fs_monitor_size fs_monitor_sizes_base[] = + { + {{640, 480}}, /* 4:3 */ + {{800, 600}}, /* 4:3 */ +@@ -73,6 +73,14 @@ fs_monitor_sizes[] = + {{1280, 768}, TRUE }, + }; + ++/* The order should be in sync with the values in 'fs_hack_is_fsr_single_mode'*/ ++static float fsr_ratios[] = { ++ 2.0f, /* FSR Performance */ ++ 1.7f, /* FSR Balanced */ ++ 1.5f, /* FSR Quality */ ++ 1.3f, /* FSR Ultra Quality */ ++}; ++ + /* A fake monitor for the fullscreen hack */ + struct fs_monitor + { +@@ -270,6 +278,55 @@ static void modes_append( DEVMODEW *modes, UINT *mode_count, UINT *resolutions, + *mode_count = *mode_count + 1; + } + ++static BOOL fs_hack_is_fsr_single_mode(UINT *mode) ++{ ++ const char *e; ++ ++ e = getenv("WINE_FULLSCREEN_FSR_MODE"); ++ if (e) ++ { ++ /* If empty or zero don't apply a mode */ ++ if (*e == '\0' || *e == '0') ++ return FALSE; ++ /* The 'mode' values should be in sync with the order in 'fsr_ratios' */ ++ if (!strcmp(e, "Ultra") || !strcmp(e, "ultra")) ++ *mode = 3; ++ else if (!strcmp(e, "Quality") || !strcmp(e, "quality")) ++ *mode = 2; ++ else if (!strcmp(e, "Balanced") || !strcmp(e, "balanced")) ++ *mode = 1; ++ else if (!strcmp(e, "Performance") || !strcmp(e, "performance")) ++ *mode = 0; ++ /* If the user mistyped the mode, return 'balanced' */ ++ else ++ *mode = 1; ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++static BOOL fs_hack_is_fsr_custom_mode(struct fs_monitor_size *fsr_custom_size) ++{ ++ DWORD width, height; ++ const char *e; ++ ++ width = 0; ++ height = 0; ++ e = getenv("WINE_FULLSCREEN_FSR_CUSTOM_MODE"); ++ if (e) ++ { ++ const int n = sscanf(e, "%dx%d", &width, &height); ++ if (n==2) ++ { ++ fsr_custom_size->size.cx = width; ++ fsr_custom_size->size.cy = height; ++ TRACE("found custom resolution: %dx%d\n", fsr_custom_size->size.cx, fsr_custom_size->size.cy); ++ return TRUE; ++ } ++ } ++ return FALSE; ++} ++ + static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UINT *mode_count ) + { + UINT i, j, max_count, real_mode_count, resolutions = 0; +@@ -277,6 +334,14 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN + BOOL additional_modes = FALSE; + const char *env; + ++ /* Default resolutions + FSR resolutions + Custom resolution */ ++ struct fs_monitor_size fs_monitor_sizes[ARRAY_SIZE(fs_monitor_sizes_base) + ARRAY_SIZE(fsr_ratios) + 1] = { {{1920, 1080}, TRUE }, }; ++ struct fs_monitor_size fs_monitor_sizes_fsr[ARRAY_SIZE(fsr_ratios)] = { {{1477, 831}, TRUE }, }; ++ struct fs_monitor_size fsr_custom_size = { {{1477, 831}, TRUE }, }; ++ UINT fs_monitor_sizes_count, fsr_mode; ++ float sharpness; ++ BOOL is_fsr, is_fsr_single_mode, is_fsr_custom_mode; ++ + *mode_count = 0; + *modes = NULL; + +@@ -284,6 +349,57 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN + /* Fullscreen hack doesn't support changing display orientations */ + if (!real_settings_handler.get_modes( monitor->adapter_id, 0, &real_modes, &real_mode_count )) return; + ++ is_fsr = fs_hack_is_fsr(&sharpness); ++ is_fsr_single_mode = FALSE; ++ is_fsr_custom_mode = FALSE; ++ ++ fs_monitor_sizes_count = 0; ++ ++ /* If FSR is enabled, generate and add FSR resolutions */ ++ if (is_fsr) ++ { ++ for (i = 0; i < ARRAY_SIZE(fs_monitor_sizes_fsr); ++i) ++ { ++ fs_monitor_sizes_fsr[i].size.cx = (DWORD)(mode_host.dmPelsWidth / fsr_ratios[i] + 0.5f); ++ fs_monitor_sizes_fsr[i].size.cy = (DWORD)(mode_host.dmPelsHeight / fsr_ratios[i] + 0.5f); ++ ++ TRACE("created fsr resolution: %dx%d, ratio: %1.1f\n", ++ fs_monitor_sizes_fsr[i].size.cx, ++ fs_monitor_sizes_fsr[i].size.cy, ++ fsr_ratios[i]); ++ } ++ ++ is_fsr_single_mode = fs_hack_is_fsr_single_mode(&fsr_mode); ++ /* If the user requested a single mode, only add that to the list */ ++ if (is_fsr_single_mode) ++ { ++ memcpy(fs_monitor_sizes+fs_monitor_sizes_count, &fs_monitor_sizes_fsr[fsr_mode], sizeof(fs_monitor_sizes_fsr[fsr_mode])); ++ fs_monitor_sizes_count += 1; ++ /* Also place it in the custom resolution container, so we can limit resolutions later on */ ++ fsr_custom_size.size.cx = fs_monitor_sizes_fsr[fsr_mode].size.cx; ++ fsr_custom_size.size.cy = fs_monitor_sizes_fsr[fsr_mode].size.cy; ++ } ++ /* If a single mode was not specified, add all FSR resolutions */ ++ else ++ { ++ memcpy(fs_monitor_sizes+fs_monitor_sizes_count, fs_monitor_sizes_fsr, sizeof(fs_monitor_sizes_fsr)); ++ fs_monitor_sizes_count += ARRAY_SIZE(fs_monitor_sizes_fsr); ++ } ++ ++ /* Add the custom resolution to the list */ ++ is_fsr_custom_mode = fs_hack_is_fsr_custom_mode(&fsr_custom_size); ++ if (is_fsr_custom_mode) ++ { ++ memcpy(fs_monitor_sizes + fs_monitor_sizes_count, &fsr_custom_size, sizeof(fsr_custom_size)); ++ fs_monitor_sizes_count += 1; ++ TRACE("added custom resolution: %dx%d\n", fsr_custom_size.size.cx, fsr_custom_size.size.cy); ++ } ++ } ++ ++ /* Copy the default list */ ++ memcpy(fs_monitor_sizes+fs_monitor_sizes_count, fs_monitor_sizes_base, sizeof(fs_monitor_sizes_base)); ++ fs_monitor_sizes_count += ARRAY_SIZE(fs_monitor_sizes_base); ++ + max_count = ARRAY_SIZE(fs_monitor_sizes) * DEPTH_COUNT + real_mode_count; + if (!(*modes = calloc( max_count, sizeof(DEVMODEW) ))) + { +@@ -300,7 +416,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN + additional_modes = !strcmp( env, "979400" ); + + /* Linux reports far fewer resolutions than Windows. Add modes that some games may expect. */ +- for (i = 0; i < ARRAY_SIZE(fs_monitor_sizes); ++i) ++ for (i = 0; i < fs_monitor_sizes_count; ++i) + { + DEVMODEW mode = mode_host; + +@@ -322,6 +438,12 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN + if (mode.dmPelsWidth > mode_host.dmPelsWidth) continue; + if (mode.dmPelsHeight > mode_host.dmPelsHeight) continue; + ++ /* Don't report modes that are larger than the requested fsr mode or the custom mode */ ++ if(is_fsr && (is_fsr_custom_mode || is_fsr_single_mode)) { ++ if (mode.dmPelsWidth < fsr_custom_size.size.cx) continue; ++ if (mode.dmPelsHeight < fsr_custom_size.size.cy) continue; ++ } ++ + for (j = 0; j < DEPTH_COUNT; ++j) + { + mode.dmBitsPerPel = depths[j]; +@@ -330,6 +452,8 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN + } + } + ++ /* report real modes only if FSR is not used */ ++ if(!is_fsr) + for (i = 0, real_mode = real_modes; i < real_mode_count; ++i) + { + DEVMODEW mode = *real_mode; +@@ -604,6 +728,28 @@ BOOL fs_hack_is_integer(void) + return is_int; + } + ++BOOL fs_hack_is_fsr(float *sharpness) ++{ ++ static int is_fsr = -1; ++ int sharpness_int = 2; ++ if (is_fsr < 0) ++ { ++ const char *e = getenv("WINE_FULLSCREEN_FSR"); ++ is_fsr = e && strcmp(e, "0"); ++ } ++ if (sharpness) ++ { ++ const char *e = getenv("WINE_FULLSCREEN_FSR_STRENGTH"); ++ if (e) ++ { ++ sharpness_int = atoi(e); ++ } ++ *sharpness = (float) sharpness_int / 10.0f; ++ } ++ TRACE("is_fsr: %s, sharpness: %2.4f\n", is_fsr ? "TRUE" : "FALSE", sharpness ? *sharpness : 0.0f); ++ return is_fsr; ++} ++ + HMONITOR fs_hack_monitor_from_rect( const RECT *in_rect ) + { + RECT rect = *in_rect; +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index f2a76e2d920..e4f91a35849 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -1133,7 +1133,7 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + } + + static VkBool32 X11DRV_query_fs_hack( VkSurfaceKHR surface, VkExtent2D *real_sz, +- VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter ) ++ VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter , BOOL *fsr, float *sharpness) + { + struct wine_vk_surface *x11_surface = surface_from_handle( surface ); + HMONITOR monitor; +@@ -1191,6 +1191,9 @@ static VkBool32 X11DRV_query_fs_hack( VkSurfaceKHR surface, VkExtent2D *real_sz, + + if (filter) *filter = fs_hack_is_integer() ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; + ++ if (fsr) ++ *fsr = fs_hack_is_fsr(sharpness); ++ + return VK_TRUE; + } + else if (fs_hack_enabled( monitor )) +@@ -1222,6 +1225,9 @@ static VkBool32 X11DRV_query_fs_hack( VkSurfaceKHR surface, VkExtent2D *real_sz, + + if (filter) *filter = fs_hack_is_integer() ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; + ++ if (fsr) ++ *fsr = fs_hack_is_fsr(sharpness); ++ + return VK_TRUE; + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 37112b80903..6dd4d5309b1 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -702,6 +702,7 @@ extern BOOL wm_is_steamcompmgr(Display *) DECLSPEC_HIDDEN; + extern BOOL fs_hack_enabled( HMONITOR monitor ) DECLSPEC_HIDDEN; + extern BOOL fs_hack_mapping_required( HMONITOR monitor ) DECLSPEC_HIDDEN; + extern BOOL fs_hack_is_integer(void) DECLSPEC_HIDDEN; ++extern BOOL fs_hack_is_fsr(float *sharpness) DECLSPEC_HIDDEN; + extern HMONITOR fs_hack_monitor_from_hwnd( HWND hwnd ) DECLSPEC_HIDDEN; + extern HMONITOR fs_hack_monitor_from_rect( const RECT *rect ) DECLSPEC_HIDDEN; + extern BOOL fs_hack_matches_current_mode( HMONITOR monitor, INT width, INT height ) DECLSPEC_HIDDEN; +-- +2.39.3 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/d3dx11_43-D3DX11CreateTextureFromMemory/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/d3dx11_43-D3DX11CreateTextureFromMemory/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/inseng-Implementation/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/inseng-Implementation/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/inseng-Implementation/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/inseng-Implementation/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0001-ntdll-Implement-retrieving-DOS-attributes-in-fd_-get.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0003-ntdll-Implement-storing-DOS-attributes-in-NtSetInfor.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0004-ntdll-Implement-storing-DOS-attributes-in-NtCreateFi.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0005-libport-Add-support-for-Mac-OS-X-style-extended-attr.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0006-libport-Add-support-for-FreeBSD-style-extended-attri.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0007-ntdll-Perform-the-Unix-style-hidden-file-check-withi.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/0008-ntdll-Always-store-SAMBA_XATTR_DOS_ATTRIB-when-path-.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-DOS_Attributes/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-DOS_Attributes/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-RtlQueryPackageIdentity/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-RtlQueryPackageIdentity/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-RtlQueryPackageIdentity/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-RtlQueryPackageIdentity/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch similarity index 82% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch index efe7c070b..2b8bb9378 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch @@ -1,4 +1,4 @@ -From 6684c0f0f73c1664c923ba150e1cb663704d8991 Mon Sep 17 00:00:00 2001 +From 0c71b9c48afdc0478941417595998ab21fcf12ae Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 29 Dec 2015 00:48:02 -0700 Subject: [PATCH] mountmgr.sys: Do a device check before returning a default @@ -12,10 +12,10 @@ Fixes https://bugs.winehq.org/show_bug.cgi?id=39793 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c -index 8c2808bb643..57ae874b247 100644 +index 328b0b2f344..0757036c8e3 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c -@@ -1868,7 +1868,7 @@ static BOOL create_port_device( DRIVER_OBJECT *driver, int n, const char *unix_p +@@ -1884,7 +1884,7 @@ static BOOL create_port_device( DRIVER_OBJECT *driver, int n, const char *unix_p UNICODE_STRING nt_name, symlink_name, default_name; DEVICE_OBJECT *dev_obj; NTSTATUS status; @@ -25,19 +25,18 @@ index 8c2808bb643..57ae874b247 100644 /* create DOS device */ if (MOUNTMGR_CALL( set_dosdev_symlink, ¶ms )) return FALSE; diff --git a/dlls/mountmgr.sys/unixlib.c b/dlls/mountmgr.sys/unixlib.c -index 73735c22d13..f83f7104d82 100644 +index 13f6fbecf09..0332f6f6018 100644 --- a/dlls/mountmgr.sys/unixlib.c +++ b/dlls/mountmgr.sys/unixlib.c -@@ -30,6 +30,7 @@ - #include - #include - #include +@@ -36,6 +36,7 @@ + #ifdef HAVE_SYS_STATVFS_H + # include + #endif +#include #include - + #include "unixlib.h" - -@@ -268,6 +269,27 @@ static NTSTATUS set_dosdev_symlink( void *args ) +@@ -304,6 +305,27 @@ static NTSTATUS set_dosdev_symlink( void *args ) char *path; NTSTATUS status = STATUS_SUCCESS; @@ -66,10 +65,10 @@ index 73735c22d13..f83f7104d82 100644 if (params->dest && params->dest[0]) diff --git a/dlls/mountmgr.sys/unixlib.h b/dlls/mountmgr.sys/unixlib.h -index 188cf93b091..31f5e8a807e 100644 +index d70371876fa..ef5b10732f6 100644 --- a/dlls/mountmgr.sys/unixlib.h +++ b/dlls/mountmgr.sys/unixlib.h -@@ -75,6 +75,7 @@ struct set_dosdev_symlink_params +@@ -90,6 +90,7 @@ struct set_dosdev_symlink_params { const char *dev; const char *dest; @@ -78,5 +77,5 @@ index 188cf93b091..31f5e8a807e 100644 struct get_volume_dos_devices_params -- -2.33.0 +2.37.2 diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-Serial_Port_Detection/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/ntdll-Serial_Port_Detection/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/ntdll-Serial_Port_Detection/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/nvcuda/0016-nvcuda-Make-nvcuda-attempt-to-load-libcuda.so.1.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/packager-DllMain/0001-packager-Prefer-native-version.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/packager-DllMain/0001-packager-Prefer-native-version.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/packager-DllMain/0001-packager-Prefer-native-version.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/packager-DllMain/0001-packager-Prefer-native-version.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/packager-DllMain/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/packager-DllMain/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/packager-DllMain/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/packager-DllMain/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/windows.networking.connectivity-new-dll/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/windows.networking.connectivity-new-dll/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winemenubuilder-Desktop_Icon_Path/0001-winemenubuilder-Create-desktop-shortcuts-with-absolu.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winex11-Vulkan_support/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winex11-Vulkan_support/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/winex11-Vulkan_support/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/winex11-Vulkan_support/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/wscript-support-d-u-switches/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/wscript-support-d-u-switches/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/wscript-support-d-u-switches/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/wscript-support-d-u-switches/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine-initial/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine-initial/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine-initial/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine-initial/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0001-Add-support-for-private-contexts.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0002-xactengine3_7-notifications.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0003-Send-NOTIFY_CUESTOP-when-Stop-is-called.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/0004-xactengine3_7-Don-t-use-switch-with-constant-integer.patch diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/definition similarity index 100% rename from wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/xactengine3_7-callbacks/definition rename to wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/70/xactengine3_7-callbacks/definition diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/0001-cryptext-Implement-CryptExtOpenCER.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/0001-cryptext-Implement-CryptExtOpenCER.patch new file mode 100644 index 000000000..65330304e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/0001-cryptext-Implement-CryptExtOpenCER.patch @@ -0,0 +1,231 @@ +From a5045503cf3310058cc64814ff9626f4877a13bb Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Fri, 5 Jul 2019 13:20:23 +0800 +Subject: [PATCH] cryptext: Implement CryptExtOpenCER. + +Signed-off-by: Dmitry Timoshkov +--- + configure | 1 + + configure.ac | 1 + + dlls/cryptext/Makefile.in | 3 +- + dlls/cryptext/cryptext.spec | 4 +-- + dlls/cryptext/cryptext_main.c | 64 +++++++++++++++++++++++++++++++++ + dlls/cryptext/tests/Makefile.in | 4 +++ + dlls/cryptext/tests/cryptext.c | 61 +++++++++++++++++++++++++++++++ + 7 files changed, 135 insertions(+), 3 deletions(-) + create mode 100644 dlls/cryptext/tests/Makefile.in + create mode 100644 dlls/cryptext/tests/cryptext.c + +diff --git a/configure b/configure +index db592f0868d..ba13abacc46 100755 +--- a/configure ++++ b/configure +@@ -20291,6 +20291,7 @@ wine_fn_config_makefile dlls/crypt32/tests enable_tests + wine_fn_config_makefile dlls/cryptdlg enable_cryptdlg + wine_fn_config_makefile dlls/cryptdll enable_cryptdll + wine_fn_config_makefile dlls/cryptext enable_cryptext ++wine_fn_config_makefile dlls/cryptext/tests enable_tests + wine_fn_config_makefile dlls/cryptnet enable_cryptnet + wine_fn_config_makefile dlls/cryptnet/tests enable_tests + wine_fn_config_makefile dlls/cryptsp enable_cryptsp +diff --git a/configure.ac b/configure.ac +index d449b88fb19..af75e0e80ab 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3029,6 +3029,7 @@ WINE_CONFIG_MAKEFILE(dlls/crypt32/tests) + WINE_CONFIG_MAKEFILE(dlls/cryptdlg) + WINE_CONFIG_MAKEFILE(dlls/cryptdll) + WINE_CONFIG_MAKEFILE(dlls/cryptext) ++WINE_CONFIG_MAKEFILE(dlls/cryptext/tests) + WINE_CONFIG_MAKEFILE(dlls/cryptnet) + WINE_CONFIG_MAKEFILE(dlls/cryptnet/tests) + WINE_CONFIG_MAKEFILE(dlls/cryptowinrt) +diff --git a/dlls/cryptext/Makefile.in b/dlls/cryptext/Makefile.in +index 0ec2b8a2045..76accca43eb 100644 +--- a/dlls/cryptext/Makefile.in ++++ b/dlls/cryptext/Makefile.in +@@ -1,4 +1,5 @@ +-MODULE = cryptext.dll ++MODULE = cryptext.dll ++IMPORTS = crypt32 cryptui user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/cryptext/cryptext.spec b/dlls/cryptext/cryptext.spec +index ee3e155f457..24b4794c198 100644 +--- a/dlls/cryptext/cryptext.spec ++++ b/dlls/cryptext/cryptext.spec +@@ -12,8 +12,8 @@ + @ stub CryptExtAddSPCW + @ stub CryptExtOpenCAT + @ stub CryptExtOpenCATW +-@ stub CryptExtOpenCER +-@ stub CryptExtOpenCERW ++@ stdcall CryptExtOpenCER(long ptr str long) ++@ stdcall CryptExtOpenCERW(long ptr wstr long) + @ stub CryptExtOpenCRL + @ stub CryptExtOpenCRLW + @ stub CryptExtOpenCTL +diff --git a/dlls/cryptext/cryptext_main.c b/dlls/cryptext/cryptext_main.c +index 537ba66cd3b..f9e34d1f8c5 100644 +--- a/dlls/cryptext/cryptext_main.c ++++ b/dlls/cryptext/cryptext_main.c +@@ -22,10 +22,29 @@ + + #include "windef.h" + #include "winbase.h" ++#include "winnls.h" ++#include "wincrypt.h" ++#include "winuser.h" ++#include "cryptuiapi.h" ++ ++#include "wine/heap.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(cryptext); + ++static WCHAR *heap_strdupAtoW(const char *str) ++{ ++ WCHAR *ret; ++ INT len; ++ ++ if (!str) return NULL; ++ len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); ++ ret = heap_alloc(len * sizeof(WCHAR)); ++ if (ret) ++ MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); ++ return ret; ++} ++ + /*********************************************************************** + * CryptExtAddPFX (CRYPTEXT.@) + */ +@@ -43,3 +62,48 @@ HRESULT WINAPI CryptExtAddPFXW(LPCWSTR filename) + FIXME("stub: %s\n", debugstr_w(filename)); + return E_NOTIMPL; + } ++ ++/*********************************************************************** ++ * CryptExtOpenCERW (CRYPTEXT.@) ++ */ ++HRESULT WINAPI CryptExtOpenCERW(HWND hwnd, HINSTANCE hinst, LPCWSTR filename, DWORD showcmd) ++{ ++ PCCERT_CONTEXT ctx; ++ CRYPTUI_VIEWCERTIFICATE_STRUCTW info; ++ ++ TRACE("(%p, %p, %s, %u)\n", hwnd, hinst, debugstr_w(filename), showcmd); ++ ++ if (!CryptQueryObject(CERT_QUERY_OBJECT_FILE, filename, CERT_QUERY_CONTENT_FLAG_CERT, ++ CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, NULL, NULL, NULL, NULL, ++ (const void **)&ctx)) ++ { ++ /* FIXME: move to the resources */ ++ static const WCHAR msg[] = {'T','h','i','s',' ','i','s',' ','n','o','t',' ','a',' ','v','a','l','i','d',' ','c','e','r','t','i','f','i','c','a','t','e',0}; ++ MessageBoxW(NULL, msg, filename, MB_OK | MB_ICONERROR); ++ return S_OK; /* according to the tests */ ++ } ++ ++ memset(&info, 0, sizeof(info)); ++ info.dwSize = sizeof(info); ++ info.pCertContext = ctx; ++ CryptUIDlgViewCertificateW(&info, NULL); ++ CertFreeCertificateContext(ctx); ++ ++ return S_OK; ++} ++ ++/*********************************************************************** ++ * CryptExtOpenCER (CRYPTEXT.@) ++ */ ++HRESULT WINAPI CryptExtOpenCER(HWND hwnd, HINSTANCE hinst, LPCSTR filename, DWORD showcmd) ++{ ++ HRESULT hr; ++ LPWSTR filenameW; ++ ++ TRACE("(%p, %p, %s, %u)\n", hwnd, hinst, debugstr_a(filename), showcmd); ++ ++ filenameW = heap_strdupAtoW(filename); ++ hr = CryptExtOpenCERW(hwnd, hinst, filenameW, showcmd); ++ heap_free(filenameW); ++ return hr; ++} +diff --git a/dlls/cryptext/tests/Makefile.in b/dlls/cryptext/tests/Makefile.in +new file mode 100644 +index 00000000000..522fc60a4af +--- /dev/null ++++ b/dlls/cryptext/tests/Makefile.in +@@ -0,0 +1,4 @@ ++TESTDLL = cryptext.dll ++ ++C_SRCS = \ ++ cryptext.c +diff --git a/dlls/cryptext/tests/cryptext.c b/dlls/cryptext/tests/cryptext.c +new file mode 100644 +index 00000000000..cc62a772b59 +--- /dev/null ++++ b/dlls/cryptext/tests/cryptext.c +@@ -0,0 +1,61 @@ ++/* ++ * Copyright 2019 Dmitry Timoshkov ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "wine/test.h" ++ ++static HRESULT (WINAPI *pCryptExtOpenCER)(HWND,HINSTANCE,LPCSTR,DWORD); ++ ++static void test_CryptExtOpenCER(void) ++{ ++ HRESULT hr; ++ ++ if (!pCryptExtOpenCER) ++ { ++ win_skip("CryptExtOpenCER is not available on this platform\n"); ++ return; ++ } ++ ++ if (!winetest_interactive) ++ { ++ skip("CryptExtOpenCER test needs user interaction\n"); ++ return; ++ } ++ ++ SetLastError(0xdeadbeef); ++ hr = pCryptExtOpenCER(0, 0, "dead.beef", SW_HIDE); ++ ok(hr == S_OK, "got %#x\n", hr); ++ ++ hr = pCryptExtOpenCER(0, 0, "VeriSign Class 3 Public Primary Certification Authority - G4.txt", SW_SHOW); ++ ok(hr == S_OK, "got %#x\n", hr); ++} ++ ++START_TEST(cryptext) ++{ ++ HMODULE hmod = LoadLibraryA("cryptext.dll"); ++ ++ pCryptExtOpenCER = (void *)GetProcAddress(hmod, "CryptExtOpenCER"); ++ ++ test_CryptExtOpenCER(); ++} +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/definition new file mode 100644 index 000000000..2b7bd7f87 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/cryptext-CryptExtOpenCER/definition @@ -0,0 +1,2 @@ +# Taken from the mailing list - July 2019. +Fixes: cryptext: Implement CryptExtOpenCER. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch new file mode 100644 index 000000000..8a33821d3 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0001-d3dx11_43-Implement-D3DX11GetImageInfoFromMemory.patch @@ -0,0 +1,254 @@ +From ea3579b5b3d701647f5c7f16de658f1cd7fe876d Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Fri, 30 Jul 2021 15:57:29 +1000 +Subject: [PATCH] d3dx11_43: Implement D3DX11GetImageInfoFromMemory + +Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=50210 + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/d3dx11_42/Makefile.in | 1 + + dlls/d3dx11_43/Makefile.in | 1 + + dlls/d3dx11_43/main.c | 9 -- + dlls/d3dx11_43/texture.c | 176 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 178 insertions(+), 9 deletions(-) + +diff --git a/dlls/d3dx11_42/Makefile.in b/dlls/d3dx11_42/Makefile.in +index 7fcce18a8e1..78ca5f707a7 100644 +--- a/dlls/d3dx11_42/Makefile.in ++++ b/dlls/d3dx11_42/Makefile.in +@@ -2,6 +2,7 @@ EXTRADEFS = -DD3DX11_SDK_VERSION=42 + MODULE = d3dx11_42.dll + IMPORTLIB = d3dx11_42 + IMPORTS = d3dcompiler ++DELAYIMPORTS = windowscodecs + PARENTSRC = ../d3dx11_43 + + EXTRADLLFLAGS = -Wb,--prefer-native +diff --git a/dlls/d3dx11_43/Makefile.in b/dlls/d3dx11_43/Makefile.in +index ccd4319ace2..6854c73ebcb 100644 +--- a/dlls/d3dx11_43/Makefile.in ++++ b/dlls/d3dx11_43/Makefile.in +@@ -2,6 +2,7 @@ EXTRADEFS = -DD3DX11_SDK_VERSION=43 + MODULE = d3dx11_43.dll + IMPORTLIB = d3dx11 + IMPORTS = d3dcompiler ++DELAYIMPORTS = windowscodecs + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/d3dx11_43/main.c b/dlls/d3dx11_43/main.c +index 5dad027864f..00c1db35e42 100644 +--- a/dlls/d3dx11_43/main.c ++++ b/dlls/d3dx11_43/main.c +@@ -66,12 +66,3 @@ HRESULT WINAPI D3DX11GetImageInfoFromFileW(const WCHAR *filename, ID3DX11ThreadP + + return E_NOTIMPL; + } +- +-HRESULT WINAPI D3DX11GetImageInfoFromMemory(const void *src_data, SIZE_T src_data_size, ID3DX11ThreadPump *pump, +- D3DX11_IMAGE_INFO *img_info, HRESULT *hresult) +-{ +- FIXME("src_data %p, src_data_size %Iu, pump %p, img_info %p, hresult %p stub!\n", +- src_data, src_data_size, pump, img_info, hresult); +- +- return E_NOTIMPL; +-} +diff --git a/dlls/d3dx11_43/texture.c b/dlls/d3dx11_43/texture.c +index 81ac8ee6db7..6881eec107d 100644 +--- a/dlls/d3dx11_43/texture.c ++++ b/dlls/d3dx11_43/texture.c +@@ -15,14 +15,190 @@ + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ ++#define COBJMACROS + + #include "d3dx11.h" + #include "d3dcompiler.h" ++#include "wincodec.h" + + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(d3dx); + ++HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT sdk_version, IWICImagingFactory **imaging_factory); ++ ++static const struct ++{ ++ const GUID *wic_container_guid; ++ D3DX11_IMAGE_FILE_FORMAT d3dx_file_format; ++} ++file_formats[] = ++{ ++ { &GUID_ContainerFormatBmp, D3DX11_IFF_BMP }, ++ { &GUID_ContainerFormatJpeg, D3DX11_IFF_JPG }, ++ { &GUID_ContainerFormatPng, D3DX11_IFF_PNG }, ++ { &GUID_ContainerFormatDds, D3DX11_IFF_DDS }, ++ { &GUID_ContainerFormatTiff, D3DX11_IFF_TIFF }, ++ { &GUID_ContainerFormatGif, D3DX11_IFF_GIF }, ++ { &GUID_ContainerFormatWmp, D3DX11_IFF_WMP }, ++}; ++ ++static D3DX11_IMAGE_FILE_FORMAT wic_container_guid_to_file_format(GUID *container_format) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(file_formats); ++i) ++ { ++ if (IsEqualGUID(file_formats[i].wic_container_guid, container_format)) ++ return file_formats[i].d3dx_file_format; ++ } ++ return D3DX11_IFF_FORCE_DWORD; ++} ++ ++static D3D11_RESOURCE_DIMENSION wic_dimension_to_d3dx11_dimension(WICDdsDimension wic_dimension) ++{ ++ switch (wic_dimension) ++ { ++ case WICDdsTexture1D: ++ return D3D11_RESOURCE_DIMENSION_TEXTURE1D; ++ case WICDdsTexture2D: ++ case WICDdsTextureCube: ++ return D3D11_RESOURCE_DIMENSION_TEXTURE2D; ++ case WICDdsTexture3D: ++ return D3D11_RESOURCE_DIMENSION_TEXTURE3D; ++ default: ++ return D3D11_RESOURCE_DIMENSION_UNKNOWN; ++ } ++} ++ ++static const DXGI_FORMAT to_be_converted_format[] = ++{ ++ DXGI_FORMAT_UNKNOWN, ++ DXGI_FORMAT_R8_UNORM, ++ DXGI_FORMAT_R8G8_UNORM, ++ DXGI_FORMAT_B5G6R5_UNORM, ++ DXGI_FORMAT_B4G4R4A4_UNORM, ++ DXGI_FORMAT_B5G5R5A1_UNORM, ++ DXGI_FORMAT_B8G8R8X8_UNORM, ++ DXGI_FORMAT_B8G8R8A8_UNORM ++}; ++ ++static DXGI_FORMAT get_d3dx11_dds_format(DXGI_FORMAT format) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(to_be_converted_format); ++i) ++ { ++ if (format == to_be_converted_format[i]) ++ return DXGI_FORMAT_R8G8B8A8_UNORM; ++ } ++ return format; ++} ++ ++HRESULT WINAPI D3DX11GetImageInfoFromMemory(const void *src_data, SIZE_T src_data_size, ID3DX11ThreadPump *pump, ++ D3DX11_IMAGE_INFO *img_info, HRESULT *hresult) ++{ ++ IWICBitmapFrameDecode *frame = NULL; ++ IWICImagingFactory *factory = NULL; ++ IWICDdsDecoder *dds_decoder = NULL; ++ IWICBitmapDecoder *decoder = NULL; ++ WICDdsParameters dds_params; ++ IWICStream *stream = NULL; ++ unsigned int frame_count; ++ GUID container_format; ++ HRESULT hr; ++ ++ TRACE("src_data %p, src_data_size %Iu, pump %p, img_info %p, hresult %p.\n", ++ src_data, src_data_size, pump, img_info, hresult); ++ ++ if (!src_data || !src_data_size || !img_info) ++ return E_FAIL; ++ if (pump) ++ FIXME("Thread pump is not supported yet.\n"); ++ ++ WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); ++ IWICImagingFactory_CreateStream(factory, &stream); ++ hr = IWICStream_InitializeFromMemory(stream, (BYTE *)src_data, src_data_size); ++ if (FAILED(hr)) ++ { ++ WARN("Failed to initialize stream.\n"); ++ goto end; ++ } ++ hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream *)stream, NULL, 0, &decoder); ++ if (FAILED(hr)) ++ goto end; ++ ++ hr = IWICBitmapDecoder_GetContainerFormat(decoder, &container_format); ++ if (FAILED(hr)) ++ goto end; ++ img_info->ImageFileFormat = wic_container_guid_to_file_format(&container_format); ++ if (img_info->ImageFileFormat == D3DX11_IFF_FORCE_DWORD) ++ { ++ hr = E_FAIL; ++ WARN("Unsupported image file format %s.\n", debugstr_guid(&container_format)); ++ goto end; ++ } ++ ++ hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); ++ if (FAILED(hr) || !frame_count) ++ goto end; ++ hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame); ++ if (FAILED(hr)) ++ goto end; ++ hr = IWICBitmapFrameDecode_GetSize(frame, &img_info->Width, &img_info->Height); ++ if (FAILED(hr)) ++ goto end; ++ ++ if (img_info->ImageFileFormat == D3DX11_IFF_DDS) ++ { ++ hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICDdsDecoder, (void **)&dds_decoder); ++ if (FAILED(hr)) ++ goto end; ++ hr = IWICDdsDecoder_GetParameters(dds_decoder, &dds_params); ++ if (FAILED(hr)) ++ goto end; ++ img_info->ArraySize = dds_params.ArraySize; ++ img_info->Depth = dds_params.Depth; ++ img_info->MipLevels = dds_params.MipLevels; ++ img_info->ResourceDimension = wic_dimension_to_d3dx11_dimension(dds_params.Dimension); ++ img_info->Format = get_d3dx11_dds_format(dds_params.DxgiFormat); ++ img_info->MiscFlags = 0; ++ if (dds_params.Dimension == WICDdsTextureCube) ++ { ++ img_info->MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; ++ img_info->ArraySize *= 6; ++ } ++ } ++ else ++ { ++ img_info->ArraySize = 1; ++ img_info->Depth = 1; ++ img_info->MipLevels = 1; ++ img_info->ResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; ++ img_info->Format = DXGI_FORMAT_R8G8B8A8_UNORM; ++ img_info->MiscFlags = 0; ++ } ++ ++end: ++ if (dds_decoder) ++ IWICDdsDecoder_Release(dds_decoder); ++ if (frame) ++ IWICBitmapFrameDecode_Release(frame); ++ if (decoder) ++ IWICBitmapDecoder_Release(decoder); ++ if (stream) ++ IWICStream_Release(stream); ++ if (factory) ++ IWICImagingFactory_Release(factory); ++ ++ if (hr != S_OK) ++ { ++ WARN("Invalid or unsupported image file.\n"); ++ return E_FAIL; ++ } ++ return S_OK; ++} ++ + HRESULT WINAPI D3DX11CreateShaderResourceViewFromMemory(ID3D11Device *device, const void *data, + SIZE_T data_size, D3DX11_IMAGE_LOAD_INFO *load_info, ID3DX11ThreadPump *pump, + ID3D11ShaderResourceView **view, HRESULT *hresult) +-- +2.34.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch new file mode 100644 index 000000000..5861e406e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/0002-d3dx11_42-Implement-D3DX11CreateTextureFromMemory.patch @@ -0,0 +1,395 @@ +From 5be34c9e347d4379179eeba742b25986152d4e4f Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 3 Aug 2021 11:13:18 +1000 +Subject: [PATCH] d3dx11_42: Implement D3DX11CreateTextureFromMemory + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/d3dx11_43/texture.c | 347 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 342 insertions(+), 5 deletions(-) + +diff --git a/dlls/d3dx11_43/texture.c b/dlls/d3dx11_43/texture.c +index 6881eec107d..b91bd8d881a 100644 +--- a/dlls/d3dx11_43/texture.c ++++ b/dlls/d3dx11_43/texture.c +@@ -22,6 +22,7 @@ + #include "wincodec.h" + + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(d3dx); + +@@ -43,6 +44,32 @@ file_formats[] = + { &GUID_ContainerFormatWmp, D3DX11_IFF_WMP }, + }; + ++static const struct ++{ ++ const GUID *wic_guid; ++ DXGI_FORMAT dxgi_format; ++} ++wic_pixel_formats[] = ++{ ++ { &GUID_WICPixelFormatBlackWhite, DXGI_FORMAT_R1_UNORM }, ++ { &GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM }, ++ { &GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM }, ++ { &GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM }, ++ { &GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT }, ++ { &GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT }, ++ { &GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM }, ++ { &GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM }, ++ { &GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, ++ { &GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, ++ { &GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM }, ++ { &GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM }, ++ { &GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, ++ { &GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM }, ++ { &GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT }, ++ { &GUID_WICPixelFormat96bppRGBFloat, DXGI_FORMAT_R32G32B32_FLOAT }, ++ { &GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT } ++}; ++ + static D3DX11_IMAGE_FILE_FORMAT wic_container_guid_to_file_format(GUID *container_format) + { + unsigned int i; +@@ -95,6 +122,175 @@ static DXGI_FORMAT get_d3dx11_dds_format(DXGI_FORMAT format) + return format; + } + ++static const DXGI_FORMAT block_compressed_formats[] = ++{ ++ DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM_SRGB, ++ DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM_SRGB, ++ DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM_SRGB, ++ DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_UNORM, DXGI_FORMAT_BC4_SNORM, ++ DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM, DXGI_FORMAT_BC5_SNORM, ++ DXGI_FORMAT_BC6H_TYPELESS, DXGI_FORMAT_BC6H_UF16, DXGI_FORMAT_BC6H_SF16, ++ DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM_SRGB ++}; ++ ++static BOOL is_block_compressed(DXGI_FORMAT format) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(block_compressed_formats); ++i) ++ if (format == block_compressed_formats[i]) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++static unsigned int get_bpp_from_format(DXGI_FORMAT format) ++{ ++ switch (format) ++ { ++ case DXGI_FORMAT_R32G32B32A32_TYPELESS: ++ case DXGI_FORMAT_R32G32B32A32_FLOAT: ++ case DXGI_FORMAT_R32G32B32A32_UINT: ++ case DXGI_FORMAT_R32G32B32A32_SINT: ++ return 128; ++ case DXGI_FORMAT_R32G32B32_TYPELESS: ++ case DXGI_FORMAT_R32G32B32_FLOAT: ++ case DXGI_FORMAT_R32G32B32_UINT: ++ case DXGI_FORMAT_R32G32B32_SINT: ++ return 96; ++ case DXGI_FORMAT_R16G16B16A16_TYPELESS: ++ case DXGI_FORMAT_R16G16B16A16_FLOAT: ++ case DXGI_FORMAT_R16G16B16A16_UNORM: ++ case DXGI_FORMAT_R16G16B16A16_UINT: ++ case DXGI_FORMAT_R16G16B16A16_SNORM: ++ case DXGI_FORMAT_R16G16B16A16_SINT: ++ case DXGI_FORMAT_R32G32_TYPELESS: ++ case DXGI_FORMAT_R32G32_FLOAT: ++ case DXGI_FORMAT_R32G32_UINT: ++ case DXGI_FORMAT_R32G32_SINT: ++ case DXGI_FORMAT_R32G8X24_TYPELESS: ++ case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: ++ case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: ++ case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: ++ case DXGI_FORMAT_Y416: ++ case DXGI_FORMAT_Y210: ++ case DXGI_FORMAT_Y216: ++ return 64; ++ case DXGI_FORMAT_R10G10B10A2_TYPELESS: ++ case DXGI_FORMAT_R10G10B10A2_UNORM: ++ case DXGI_FORMAT_R10G10B10A2_UINT: ++ case DXGI_FORMAT_R11G11B10_FLOAT: ++ case DXGI_FORMAT_R8G8B8A8_TYPELESS: ++ case DXGI_FORMAT_R8G8B8A8_UNORM: ++ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: ++ case DXGI_FORMAT_R8G8B8A8_UINT: ++ case DXGI_FORMAT_R8G8B8A8_SNORM: ++ case DXGI_FORMAT_R8G8B8A8_SINT: ++ case DXGI_FORMAT_R16G16_TYPELESS: ++ case DXGI_FORMAT_R16G16_FLOAT: ++ case DXGI_FORMAT_R16G16_UNORM: ++ case DXGI_FORMAT_R16G16_UINT: ++ case DXGI_FORMAT_R16G16_SNORM: ++ case DXGI_FORMAT_R16G16_SINT: ++ case DXGI_FORMAT_R32_TYPELESS: ++ case DXGI_FORMAT_D32_FLOAT: ++ case DXGI_FORMAT_R32_FLOAT: ++ case DXGI_FORMAT_R32_UINT: ++ case DXGI_FORMAT_R32_SINT: ++ case DXGI_FORMAT_R24G8_TYPELESS: ++ case DXGI_FORMAT_D24_UNORM_S8_UINT: ++ case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: ++ case DXGI_FORMAT_X24_TYPELESS_G8_UINT: ++ case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: ++ case DXGI_FORMAT_R8G8_B8G8_UNORM: ++ case DXGI_FORMAT_G8R8_G8B8_UNORM: ++ case DXGI_FORMAT_B8G8R8A8_UNORM: ++ case DXGI_FORMAT_B8G8R8X8_UNORM: ++ case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: ++ case DXGI_FORMAT_B8G8R8A8_TYPELESS: ++ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: ++ case DXGI_FORMAT_B8G8R8X8_TYPELESS: ++ case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: ++ case DXGI_FORMAT_AYUV: ++ case DXGI_FORMAT_Y410: ++ case DXGI_FORMAT_YUY2: ++ return 32; ++ case DXGI_FORMAT_P010: ++ case DXGI_FORMAT_P016: ++ return 24; ++ case DXGI_FORMAT_R8G8_TYPELESS: ++ case DXGI_FORMAT_R8G8_UNORM: ++ case DXGI_FORMAT_R8G8_UINT: ++ case DXGI_FORMAT_R8G8_SNORM: ++ case DXGI_FORMAT_R8G8_SINT: ++ case DXGI_FORMAT_R16_TYPELESS: ++ case DXGI_FORMAT_R16_FLOAT: ++ case DXGI_FORMAT_D16_UNORM: ++ case DXGI_FORMAT_R16_UNORM: ++ case DXGI_FORMAT_R16_UINT: ++ case DXGI_FORMAT_R16_SNORM: ++ case DXGI_FORMAT_R16_SINT: ++ case DXGI_FORMAT_B5G6R5_UNORM: ++ case DXGI_FORMAT_B5G5R5A1_UNORM: ++ case DXGI_FORMAT_A8P8: ++ case DXGI_FORMAT_B4G4R4A4_UNORM: ++ return 16; ++ case DXGI_FORMAT_NV12: ++ case DXGI_FORMAT_420_OPAQUE: ++ case DXGI_FORMAT_NV11: ++ return 12; ++ case DXGI_FORMAT_R8_TYPELESS: ++ case DXGI_FORMAT_R8_UNORM: ++ case DXGI_FORMAT_R8_UINT: ++ case DXGI_FORMAT_R8_SNORM: ++ case DXGI_FORMAT_R8_SINT: ++ case DXGI_FORMAT_A8_UNORM: ++ case DXGI_FORMAT_AI44: ++ case DXGI_FORMAT_IA44: ++ case DXGI_FORMAT_P8: ++ case DXGI_FORMAT_BC2_TYPELESS: ++ case DXGI_FORMAT_BC2_UNORM: ++ case DXGI_FORMAT_BC2_UNORM_SRGB: ++ case DXGI_FORMAT_BC3_TYPELESS: ++ case DXGI_FORMAT_BC3_UNORM: ++ case DXGI_FORMAT_BC3_UNORM_SRGB: ++ case DXGI_FORMAT_BC5_TYPELESS: ++ case DXGI_FORMAT_BC5_UNORM: ++ case DXGI_FORMAT_BC5_SNORM: ++ case DXGI_FORMAT_BC6H_TYPELESS: ++ case DXGI_FORMAT_BC6H_UF16: ++ case DXGI_FORMAT_BC6H_SF16: ++ case DXGI_FORMAT_BC7_TYPELESS: ++ case DXGI_FORMAT_BC7_UNORM: ++ case DXGI_FORMAT_BC7_UNORM_SRGB: ++ return 8; ++ case DXGI_FORMAT_BC1_TYPELESS: ++ case DXGI_FORMAT_BC1_UNORM: ++ case DXGI_FORMAT_BC1_UNORM_SRGB: ++ case DXGI_FORMAT_BC4_TYPELESS: ++ case DXGI_FORMAT_BC4_UNORM: ++ case DXGI_FORMAT_BC4_SNORM: ++ return 4; ++ case DXGI_FORMAT_R1_UNORM: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static const GUID *dxgi_format_to_wic_guid(DXGI_FORMAT format) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(wic_pixel_formats); ++i) ++ { ++ if (wic_pixel_formats[i].dxgi_format == format) ++ return wic_pixel_formats[i].wic_guid; ++ } ++ ++ return NULL; ++} ++ + HRESULT WINAPI D3DX11GetImageInfoFromMemory(const void *src_data, SIZE_T src_data_size, ID3DX11ThreadPump *pump, + D3DX11_IMAGE_INFO *img_info, HRESULT *hresult) + { +@@ -229,14 +425,155 @@ HRESULT WINAPI D3DX11CreateTextureFromFileW(ID3D11Device *device, const WCHAR *f + return E_NOTIMPL; + } + +-HRESULT WINAPI D3DX11CreateTextureFromMemory(ID3D11Device *device, const void *data, +- SIZE_T data_size, D3DX11_IMAGE_LOAD_INFO *load_info, ID3DX11ThreadPump *pump, ++HRESULT WINAPI D3DX11CreateTextureFromMemory(ID3D11Device *device, const void *src_data, ++ SIZE_T src_data_size, D3DX11_IMAGE_LOAD_INFO *load_info, ID3DX11ThreadPump *pump, + ID3D11Resource **texture, HRESULT *hresult) + { +- FIXME("device %p, data %p, data_size %Iu, load_info %p, pump %p, texture %p, hresult %p stub.\n", +- device, data, data_size, load_info, pump, texture, hresult); ++ unsigned int frame_count, width, height, stride, frame_size; ++ IWICFormatConverter *converter = NULL; ++ IWICDdsFrameDecode *dds_frame = NULL; ++ D3D11_TEXTURE2D_DESC texture_2d_desc; ++ D3D11_SUBRESOURCE_DATA resource_data; ++ IWICBitmapFrameDecode *frame = NULL; ++ IWICImagingFactory *factory = NULL; ++ IWICBitmapDecoder *decoder = NULL; ++ ID3D11Texture2D *texture_2d; ++ D3DX11_IMAGE_INFO img_info; ++ IWICStream *stream = NULL; ++ const GUID *dst_format; ++ BYTE *buffer = NULL; ++ BOOL can_convert; ++ GUID src_format; ++ HRESULT hr; + +- return E_NOTIMPL; ++ TRACE("device %p, data %p, data_size %Iu, load_info %p, pump %p, texture %p, hresult %p.\n", ++ device, src_data, src_data_size, load_info, pump, texture, hresult); ++ ++ if (!src_data || !src_data_size || !texture) ++ return E_FAIL; ++ if (load_info) ++ FIXME("load_info is ignored.\n"); ++ if (pump) ++ FIXME("Thread pump is not supported yet.\n"); ++ ++ if (FAILED(D3DX11GetImageInfoFromMemory(src_data, src_data_size, NULL, &img_info, NULL))) ++ return E_FAIL; ++ if (img_info.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) ++ { ++ FIXME("Cube map is not supported.\n"); ++ return E_FAIL; ++ } ++ ++ if (FAILED(hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory))) ++ goto end; ++ if (FAILED(hr = IWICImagingFactory_CreateStream(factory, &stream))) ++ goto end; ++ if (FAILED(hr = IWICStream_InitializeFromMemory(stream, (BYTE *)src_data, src_data_size))) ++ goto end; ++ if (FAILED(hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream *)stream, NULL, 0, &decoder))) ++ goto end; ++ if (FAILED(hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count)) || !frame_count) ++ goto end; ++ if (FAILED(hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame))) ++ goto end; ++ if (FAILED(hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &src_format))) ++ goto end; ++ ++ width = img_info.Width; ++ height = img_info.Height; ++ if (is_block_compressed(img_info.Format)) ++ { ++ width = (width + 3) & ~3; ++ height = (height + 3) & ~3; ++ } ++ stride = (width * get_bpp_from_format(img_info.Format) + 7) / 8; ++ frame_size = stride * height; ++ ++ if (!(buffer = heap_alloc(frame_size))) ++ { ++ hr = E_FAIL; ++ goto end; ++ } ++ ++ if (is_block_compressed(img_info.Format)) ++ { ++ if (FAILED(hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICDdsFrameDecode, (void **)&dds_frame))) ++ goto end; ++ if (FAILED(hr = IWICDdsFrameDecode_CopyBlocks(dds_frame, NULL, stride * 4, frame_size, buffer))) ++ goto end; ++ } ++ else ++ { ++ if (!(dst_format = dxgi_format_to_wic_guid(img_info.Format))) ++ { ++ hr = E_FAIL; ++ FIXME("Unsupported DXGI format %#x.\n", img_info.Format); ++ goto end; ++ } ++ ++ if (IsEqualGUID(&src_format, dst_format)) ++ { ++ if (FAILED(hr = IWICBitmapFrameDecode_CopyPixels(frame, NULL, stride, frame_size, buffer))) ++ goto end; ++ } ++ else ++ { ++ if (FAILED(hr = IWICImagingFactory_CreateFormatConverter(factory, &converter))) ++ goto end; ++ if (FAILED(hr = IWICFormatConverter_CanConvert(converter, &src_format, dst_format, &can_convert))) ++ goto end; ++ if (!can_convert) ++ { ++ WARN("Format converting %s to %s is not supported by WIC.\n", ++ debugstr_guid(&src_format), debugstr_guid(dst_format)); ++ goto end; ++ } ++ if (FAILED(hr = IWICFormatConverter_Initialize(converter, (IWICBitmapSource *)frame, dst_format, ++ WICBitmapDitherTypeErrorDiffusion, 0, 0, WICBitmapPaletteTypeCustom))) ++ goto end; ++ if (FAILED(hr = IWICFormatConverter_CopyPixels(converter, NULL, stride, frame_size, buffer))) ++ goto end; ++ } ++ } ++ ++ memset(&texture_2d_desc, 0, sizeof(texture_2d_desc)); ++ texture_2d_desc.Width = width; ++ texture_2d_desc.Height = height; ++ texture_2d_desc.MipLevels = 1; ++ texture_2d_desc.ArraySize = img_info.ArraySize; ++ texture_2d_desc.Format = img_info.Format; ++ texture_2d_desc.SampleDesc.Count = 1; ++ texture_2d_desc.Usage = D3D11_USAGE_DEFAULT; ++ texture_2d_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; ++ texture_2d_desc.MiscFlags = img_info.MiscFlags; ++ ++ resource_data.pSysMem = buffer; ++ resource_data.SysMemPitch = stride; ++ resource_data.SysMemSlicePitch = frame_size; ++ ++ if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_2d_desc, &resource_data, &texture_2d))) ++ goto end; ++ ++ *texture = (ID3D11Resource *)texture_2d; ++ hr = S_OK; ++ ++end: ++ if (converter) ++ IWICFormatConverter_Release(converter); ++ if (dds_frame) ++ IWICDdsFrameDecode_Release(dds_frame); ++ if (buffer) ++ heap_free(buffer); ++ if (frame) ++ IWICBitmapFrameDecode_Release(frame); ++ if (decoder) ++ IWICBitmapDecoder_Release(decoder); ++ if (stream) ++ IWICStream_Release(stream); ++ if (factory) ++ IWICImagingFactory_Release(factory); ++ ++ return hr; + } + + HRESULT WINAPI D3DX11SaveTextureToFileW(ID3D11DeviceContext *context, ID3D11Resource *texture, +-- +2.34.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/definition new file mode 100644 index 000000000..e8ee507f3 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/d3dx11_43-D3DX11CreateTextureFromMemory/definition @@ -0,0 +1,4 @@ +Fixes: [50210] - Implement D3DX11GetImageInfoFromMemory +Fixes: [45533] - Implement D3DX11CreateTextureFromMemory + +# This patchset will need to wait until the new wined3dx dll implemented. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch new file mode 100644 index 000000000..e3832d19a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0001-fltmgr.sys-Implement-FltBuildDefaultSecurityDescript.patch @@ -0,0 +1,143 @@ +From f9da0ca4c7012918b5c8660ebe8a9ea0c74f05b0 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Sun, 29 Aug 2021 13:26:53 +1000 +Subject: [PATCH] fltmgr.sys: Implement FltBuildDefaultSecurityDescriptor + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/fltmgr.sys/Makefile.in | 1 + + dlls/fltmgr.sys/fltmgr.sys.spec | 4 +- + dlls/fltmgr.sys/main.c | 71 +++++++++++++++++++++++++++++++++ + include/ddk/fltkernel.h | 3 +- + 4 files changed, 76 insertions(+), 3 deletions(-) + +diff --git a/dlls/fltmgr.sys/Makefile.in b/dlls/fltmgr.sys/Makefile.in +index ba106a43831..bb1f34b4896 100644 +--- a/dlls/fltmgr.sys/Makefile.in ++++ b/dlls/fltmgr.sys/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = fltmgr.sys + EXTRADLLFLAGS = -Wl,--subsystem,native ++IMPORTS = ntoskrnl + + C_SRCS = \ + main.c +diff --git a/dlls/fltmgr.sys/fltmgr.sys.spec b/dlls/fltmgr.sys/fltmgr.sys.spec +index 39ce6798178..8943b9f85cf 100644 +--- a/dlls/fltmgr.sys/fltmgr.sys.spec ++++ b/dlls/fltmgr.sys/fltmgr.sys.spec +@@ -10,7 +10,7 @@ + @ stub FltAllocatePoolAlignedWithTag + @ stub FltAttachVolume + @ stub FltAttachVolumeAtAltitude +-@ stub FltBuildDefaultSecurityDescriptor ++@ stdcall FltBuildDefaultSecurityDescriptor(ptr long) + @ stub FltCancelFileOpen + @ stub FltCancelIo + @ stub FltCbdqDisable +@@ -60,7 +60,7 @@ + @ stub FltFreeFileLock + @ stub FltFreeGenericWorkItem + @ stub FltFreePoolAlignedWithTag +-@ stub FltFreeSecurityDescriptor ++@ stdcall FltFreeSecurityDescriptor(ptr) + @ stub FltFsControlFile + @ stub FltGetBottomInstance + @ stub FltGetContexts +diff --git a/dlls/fltmgr.sys/main.c b/dlls/fltmgr.sys/main.c +index e1016a4989c..ea9685b4308 100644 +--- a/dlls/fltmgr.sys/main.c ++++ b/dlls/fltmgr.sys/main.c +@@ -93,3 +93,74 @@ void* WINAPI FltGetRoutineAddress(LPCSTR name) + + return func; + } ++ ++NTSTATUS WINAPI FltBuildDefaultSecurityDescriptor(PSECURITY_DESCRIPTOR *descriptor, ACCESS_MASK access) ++{ ++ PACL dacl; ++ NTSTATUS ret = STATUS_INSUFFICIENT_RESOURCES; ++ ULONG sid_len; ++ PSID sid; ++ PSID sid_system; ++ PSECURITY_DESCRIPTOR sec_desc = NULL; ++ SID_IDENTIFIER_AUTHORITY auth = { SECURITY_NULL_SID_AUTHORITY }; ++ ++ *descriptor = NULL; ++ ++ ret = RtlAllocateAndInitializeSid(&auth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ADMINS, ++ 0, 0, 0, 0, 0, 0, &sid); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ ret = RtlAllocateAndInitializeSid(&auth, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &sid_system); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ sid_len = SECURITY_DESCRIPTOR_MIN_LENGTH + sizeof(ACL) + ++ sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(sid) + ++ sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(sid_system); ++ ++ sec_desc = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sid_len); ++ if (!sec_desc) ++ { ++ ret = STATUS_NO_MEMORY; ++ goto done; ++ } ++ ++ ret = RtlCreateSecurityDescriptor(sec_desc, SECURITY_DESCRIPTOR_REVISION); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ dacl = (PACL)((char*)sec_desc + SECURITY_DESCRIPTOR_MIN_LENGTH); ++ ret = RtlCreateAcl(dacl, sid_len - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ ret = RtlAddAccessAllowedAce(dacl, ACL_REVISION, access, sid); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ ret = RtlAddAccessAllowedAce(dacl, ACL_REVISION, access, sid_system); ++ if (ret != STATUS_SUCCESS) ++ goto done; ++ ++ ret = RtlSetDaclSecurityDescriptor(sec_desc, 1, dacl, 0); ++ if (ret == STATUS_SUCCESS) ++ *descriptor = sec_desc; ++ ++done: ++ if (ret != STATUS_SUCCESS && sec_desc != NULL) ++ RtlFreeHeap(GetProcessHeap(), 0, sec_desc); ++ ++ if (sid != NULL) ++ RtlFreeHeap(GetProcessHeap(), 0, sid); ++ ++ if (sid_system != NULL) ++ RtlFreeHeap(GetProcessHeap(), 0, sid_system); ++ ++ return ret; ++} ++ ++void WINAPI FltFreeSecurityDescriptor(PSECURITY_DESCRIPTOR descriptor) ++{ ++ RtlFreeHeap(GetProcessHeap(), 0, descriptor); ++} +\ No newline at end of file +diff --git a/include/ddk/fltkernel.h b/include/ddk/fltkernel.h +index 8ebebfa2e81..9ece0990810 100644 +--- a/include/ddk/fltkernel.h ++++ b/include/ddk/fltkernel.h +@@ -653,7 +653,8 @@ typedef struct _FLT_REGISTRATION + PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback; + } FLT_REGISTRATION, *PFLT_REGISTRATION; + +- ++NTSTATUS WINAPI FltBuildDefaultSecurityDescriptor(PSECURITY_DESCRIPTOR *, ACCESS_MASK); ++void WINAPI FltFreeSecurityDescriptor(PSECURITY_DESCRIPTOR); + void* WINAPI FltGetRoutineAddress(LPCSTR name); + NTSTATUS WINAPI FltRegisterFilter(PDRIVER_OBJECT, const FLT_REGISTRATION *, PFLT_FILTER *); + NTSTATUS WINAPI FltStartFiltering(PFLT_FILTER); +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch new file mode 100644 index 000000000..57aab8bc9 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0002-fltmgr.sys-Create-import-library.patch @@ -0,0 +1,23 @@ +From 36bb7032734a97c5b9d01ef96d595973ea16eb95 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Mon, 30 Aug 2021 15:15:35 +1000 +Subject: [PATCH] fltmgr.sys: Create import library + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/fltmgr.sys/Makefile.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/fltmgr.sys/Makefile.in b/dlls/fltmgr.sys/Makefile.in +index bb1f34b4896..5540df35d6a 100644 +--- a/dlls/fltmgr.sys/Makefile.in ++++ b/dlls/fltmgr.sys/Makefile.in +@@ -1,4 +1,5 @@ + MODULE = fltmgr.sys ++IMPORTLIB = fltmgr + EXTRADLLFLAGS = -Wl,--subsystem,native + IMPORTS = ntoskrnl + +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch new file mode 100644 index 000000000..f37f2789f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/0003-ntoskrnl.exe-Add-FltBuildDefaultSecurityDescriptor-t.patch @@ -0,0 +1,117 @@ +From ba211cf9d8ca7a462c24a62334813c68d41b3fc0 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Mon, 30 Aug 2021 15:16:06 +1000 +Subject: [PATCH] ntoskrnl.exe: Add FltBuildDefaultSecurityDescriptor test + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/ntoskrnl.exe/tests/Makefile.in | 2 +- + dlls/ntoskrnl.exe/tests/driver.c | 65 +++++++++++++++++++++++++++++ + 2 files changed, 66 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in +index 052a8618a81..9028a392002 100644 +--- a/dlls/ntoskrnl.exe/tests/Makefile.in ++++ b/dlls/ntoskrnl.exe/tests/Makefile.in +@@ -2,7 +2,7 @@ EXTRADEFS = -DWINE_NO_LONG_TYPES + TESTDLL = ntoskrnl.exe + IMPORTS = advapi32 crypt32 newdev setupapi user32 wintrust ws2_32 hid + +-driver_IMPORTS = winecrt0 ntoskrnl hal ++driver_IMPORTS = winecrt0 ntoskrnl hal fltmgr + driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native + driver2_IMPORTS = winecrt0 ntoskrnl hal + driver2_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native +diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c +index dabb3b73f15..187f00c8bcb 100644 +--- a/dlls/ntoskrnl.exe/tests/driver.c ++++ b/dlls/ntoskrnl.exe/tests/driver.c +@@ -32,6 +32,7 @@ + #include "ddk/ntddk.h" + #include "ddk/ntifs.h" + #include "ddk/wdm.h" ++#include "ddk/fltkernel.h" + + #include "driver.h" + +@@ -2338,6 +2339,69 @@ static void test_default_modules(void) + ok(dxgmms1, "Failed to find dxgmms1.sys\n"); + } + ++static void test_default_security(void) ++{ ++ PSECURITY_DESCRIPTOR sd = NULL; ++ NTSTATUS status; ++ PSID group = NULL, owner = NULL; ++ BOOLEAN isdefault, present; ++ PACL acl = NULL; ++ PACCESS_ALLOWED_ACE ace; ++ SID_IDENTIFIER_AUTHORITY auth = { SECURITY_NULL_SID_AUTHORITY }; ++ PSID sid1, sid2; ++ ++ status = FltBuildDefaultSecurityDescriptor(&sd, STANDARD_RIGHTS_ALL); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ok(sd != NULL, "Failed to return descriptor\n"); ++ ++ status = RtlGetGroupSecurityDescriptor(sd, &group, &isdefault); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ok(group == NULL, "group isn't NULL\n"); ++ ++ status = RtlGetOwnerSecurityDescriptor(sd, &owner, &isdefault); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ok(owner == NULL, "owner isn't NULL\n"); ++ ++ status = RtlGetDaclSecurityDescriptor(sd, &present, &acl, &isdefault); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ok(acl != NULL, "acl is NULL\n"); ++ ok(acl->AceCount == 2, "got %d\n", acl->AceCount); ++ ++ sid1 = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, RtlLengthRequiredSid(2)); ++ RtlInitializeSid(sid1, &auth, 2); ++ *RtlSubAuthoritySid(sid1, 0) = SECURITY_BUILTIN_DOMAIN_RID; ++ *RtlSubAuthoritySid(sid1, 1) = DOMAIN_GROUP_RID_ADMINS; ++ ++ sid2 = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, RtlLengthRequiredSid(1)); ++ RtlInitializeSid(sid2, &auth, 1); ++ *RtlSubAuthoritySid(sid2, 0) = SECURITY_LOCAL_SYSTEM_RID; ++ ++ /* SECURITY_BUILTIN_DOMAIN_RID */ ++ status = RtlGetAce(acl, 0, (void**)&ace); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ++ ok(ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, "got %#x\n", ace->Header.AceType); ++ ok(ace->Header.AceFlags == 0, "got %#x\n", ace->Header.AceFlags); ++ ok(ace->Mask == STANDARD_RIGHTS_ALL, "got %#x\n", ace->Mask); ++ ++ ok(RtlEqualSid(sid1, (PSID)&ace->SidStart), "SID not equal\n"); ++ ++ /* SECURITY_LOCAL_SYSTEM_RID */ ++ status = RtlGetAce(acl, 1, (void**)&ace); ++ ok(status == STATUS_SUCCESS, "got %#x\n", status); ++ ++ ok(ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, "got %#x\n", ace->Header.AceType); ++ ok(ace->Header.AceFlags == 0, "got %#x\n", ace->Header.AceFlags); ++ ok(ace->Mask == STANDARD_RIGHTS_ALL, "got %#x\n", ace->Mask); ++ ++ ok(RtlEqualSid(sid2, (PSID)&ace->SidStart), "SID not equal\n"); ++ ++ RtlFreeHeap(GetProcessHeap(), 0, sid1); ++ RtlFreeHeap(GetProcessHeap(), 0, sid2); ++ ++ FltFreeSecurityDescriptor(sd); ++} ++ + static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack) + { + void *buffer = irp->AssociatedIrp.SystemBuffer; +@@ -2382,6 +2446,7 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st + test_dpc(); + test_process_memory(test_input); + test_permanence(); ++ test_default_security(); + + IoMarkIrpPending(irp); + IoQueueWorkItem(work_item, main_test_task, DelayedWorkQueue, irp); +-- +2.34.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition new file mode 100644 index 000000000..407756977 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/fltmgr.sys-FltBuildDefaultSecurityDescriptor/definition @@ -0,0 +1,2 @@ +Fixes: [49089] fltmgr.sys: Implement FltBuildDefaultSecurityDescriptor +Depends: winedevice-Default_Drivers diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch new file mode 100644 index 000000000..db22c02b7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/0001-inseng-Implement-CIF-reader-and-download-functions.patch @@ -0,0 +1,3866 @@ +From c62655b9d54ce8b692bd84d41abe09cc40ae10e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Mon, 5 Sep 2016 15:31:29 +0200 +Subject: [PATCH] inseng: Implement CIF reader and download functions. + +FIXME: Needs splitting. +--- + dlls/inseng/Makefile.in | 7 +- + dlls/inseng/icif.c | 1745 ++++++++++++++++++++++++++++++++++ + dlls/inseng/inf.c | 443 +++++++++ + dlls/inseng/inseng.spec | 4 +- + dlls/inseng/inseng_main.c | 990 ++++++++++++++++++- + dlls/inseng/inseng_private.h | 79 ++ + include/inseng.idl | 276 +++++- + 7 files changed, 3490 insertions(+), 54 deletions(-) + create mode 100644 dlls/inseng/icif.c + create mode 100644 dlls/inseng/inf.c + create mode 100644 dlls/inseng/inseng_private.h + +diff --git a/dlls/inseng/Makefile.in b/dlls/inseng/Makefile.in +index 0217203791a..ba2388c97ed 100644 +--- a/dlls/inseng/Makefile.in ++++ b/dlls/inseng/Makefile.in +@@ -1,8 +1,11 @@ + MODULE = inseng.dll +-IMPORTS = uuid ole32 advapi32 ++IMPORTS = uuid ole32 advapi32 urlmon shlwapi + + EXTRADLLFLAGS = -Wb,--prefer-native + +-C_SRCS = inseng_main.c ++C_SRCS = \ ++ icif.c \ ++ inf.c \ ++ inseng_main.c + + IDL_SRCS = inseng_classes.idl +diff --git a/dlls/inseng/icif.c b/dlls/inseng/icif.c +new file mode 100644 +index 00000000000..27f6f6dfd93 +--- /dev/null ++++ b/dlls/inseng/icif.c +@@ -0,0 +1,1745 @@ ++/* ++ * Copyright 2016 Michael MĂ¼ller ++ * ++ * 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 ++ */ ++ ++#define COBJMACROS ++ ++ ++ ++#include ++#include ++ ++#include "windef.h" ++#include "winbase.h" ++#include "winuser.h" ++#include "ole2.h" ++#include "rpcproxy.h" ++#include "inseng.h" ++ ++#include "inseng_private.h" ++ ++#include "wine/list.h" ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(inseng); ++ ++#define DEFAULT_INSTALLER_DESC "Active Setup Installation" ++ ++struct cifgroup ++{ ++ ICifGroup ICifGroup_iface; ++ ++ struct list entry; ++ ++ ICifFile *parent; ++ ++ char *id; ++ char *description; ++ DWORD priority; ++}; ++ ++struct ciffenum_components ++{ ++ IEnumCifComponents IEnumCifComponents_iface; ++ LONG ref; ++ ++ ICifFile *file; ++ struct list *start; ++ struct list *position; ++ ++ char *group_id; ++}; ++ ++struct ciffenum_groups ++{ ++ IEnumCifGroups IEnumCifGroups_iface; ++ LONG ref; ++ ++ ICifFile *file; ++ struct list *start; ++ struct list *position; ++}; ++ ++struct url_info ++{ ++ struct list entry; ++ INT index; ++ char *url; ++ DWORD flags; ++}; ++ ++struct dependency_info ++{ ++ struct list entry; ++ char *id; ++ char *type; ++}; ++ ++struct cifcomponent ++{ ++ ICifComponent ICifComponent_iface; ++ ++ struct list entry; ++ ++ ICifFile *parent; ++ ++ char *id; ++ char *guid; ++ char *description; ++ char *details; ++ char *group; ++ ++ ++ DWORD version; ++ DWORD build; ++ char *patchid; ++ ++ char *locale; ++ char *key_uninstall; ++ ++ DWORD size_win; ++ DWORD size_app; ++ DWORD size_download; ++ DWORD size_extracted; ++ ++ char *key_success; ++ char *key_progress; ++ char *key_cancel; ++ ++ DWORD as_aware; ++ DWORD reboot; ++ DWORD admin; ++ DWORD visibleui; ++ ++ DWORD priority; ++ DWORD platform; ++ ++ struct list dependencies; ++ struct list urls; ++ ++ /* mode */ ++ /* det version */ ++ /* one component */ ++ /* custom data */ ++ ++ /* in memory state */ ++ DWORD queue_state; ++ DWORD current_priority; ++ DWORD size_actual_download; ++ BOOL downloaded; ++ BOOL installed; ++}; ++ ++struct ciffile ++{ ++ ICifFile ICifFile_iface; ++ LONG ref; ++ ++ struct list components; ++ struct list groups; ++ ++ char *name; ++}; ++ ++static inline struct ciffile *impl_from_ICiffile(ICifFile *iface) ++{ ++ return CONTAINING_RECORD(iface, struct ciffile, ICifFile_iface); ++} ++ ++static inline struct cifcomponent *impl_from_ICifComponent(ICifComponent *iface) ++{ ++ return CONTAINING_RECORD(iface, struct cifcomponent, ICifComponent_iface); ++} ++ ++static inline struct cifgroup *impl_from_ICifGroup(ICifGroup *iface) ++{ ++ return CONTAINING_RECORD(iface, struct cifgroup, ICifGroup_iface); ++} ++ ++static inline struct ciffenum_components *impl_from_IEnumCifComponents(IEnumCifComponents *iface) ++{ ++ return CONTAINING_RECORD(iface, struct ciffenum_components, IEnumCifComponents_iface); ++} ++ ++static inline struct ciffenum_groups *impl_from_IEnumCifGroups(IEnumCifGroups *iface) ++{ ++ return CONTAINING_RECORD(iface, struct ciffenum_groups, IEnumCifGroups_iface); ++} ++ ++static HRESULT enum_components_create(ICifFile *file, struct list *start, char *group_id, IEnumCifComponents **iface); ++ ++static HRESULT copy_substring_null(char *dest, int max_len, char *src) ++{ ++ if (!src) ++ return E_FAIL; ++ ++ if (max_len <= 0) ++ return S_OK; ++ ++ if (!dest) ++ return E_FAIL; ++ ++ while (*src && max_len-- > 1) ++ *dest++ = *src++; ++ *dest = 0; ++ ++ return S_OK; ++} ++ ++static void url_entry_free(struct url_info *url) ++{ ++ heap_free(url->url); ++ heap_free(url); ++} ++ ++static void dependency_entry_free(struct dependency_info *dependency) ++{ ++ heap_free(dependency->id); ++ heap_free(dependency); ++} ++ ++static void component_free(struct cifcomponent *comp) ++{ ++ struct dependency_info *dependency, *dependency_next; ++ struct url_info *url, *url_next; ++ ++ heap_free(comp->id); ++ heap_free(comp->guid); ++ heap_free(comp->description); ++ heap_free(comp->details); ++ heap_free(comp->group); ++ ++ heap_free(comp->patchid); ++ ++ heap_free(comp->locale); ++ heap_free(comp->key_uninstall); ++ ++ heap_free(comp->key_success); ++ heap_free(comp->key_progress); ++ heap_free(comp->key_cancel); ++ ++ LIST_FOR_EACH_ENTRY_SAFE(dependency, dependency_next, &comp->dependencies, struct dependency_info, entry) ++ { ++ list_remove(&dependency->entry); ++ dependency_entry_free(dependency); ++ } ++ ++ LIST_FOR_EACH_ENTRY_SAFE(url, url_next, &comp->urls, struct url_info, entry) ++ { ++ list_remove(&url->entry); ++ url_entry_free(url); ++ } ++ ++ heap_free(comp); ++} ++ ++static void group_free(struct cifgroup *group) ++{ ++ heap_free(group->id); ++ heap_free(group->description); ++ heap_free(group); ++} ++ ++static HRESULT WINAPI group_GetID(ICifGroup *iface, char *id, DWORD size) ++{ ++ struct cifgroup *This = impl_from_ICifGroup(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, id, size); ++ ++ return copy_substring_null(id, size, This->id); ++} ++ ++static HRESULT WINAPI group_GetDescription(ICifGroup *iface, char *desc, DWORD size) ++{ ++ struct cifgroup *This = impl_from_ICifGroup(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, desc, size); ++ ++ return copy_substring_null(desc, size, This->description); ++} ++ ++static DWORD WINAPI group_GetPriority(ICifGroup *iface) ++{ ++ struct cifgroup *This = impl_from_ICifGroup(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->priority; ++} ++ ++static HRESULT WINAPI group_EnumComponents(ICifGroup *iface, IEnumCifComponents **enum_components, DWORD filter, LPVOID pv) ++{ ++ struct cifgroup *This = impl_from_ICifGroup(iface); ++ struct ciffile *file; ++ ++ TRACE("(%p)->(%p, %#lx, %p)\n", This, enum_components, filter, pv); ++ ++ if (filter) ++ FIXME("filter (%#lx) not supported\n", filter); ++ if (pv) ++ FIXME("how to handle pv (%p)?\n", pv); ++ ++ file = impl_from_ICiffile(This->parent); ++ return enum_components_create(This->parent, &file->components, This->id, enum_components); ++} ++ ++static DWORD WINAPI group_GetCurrentPriority(ICifGroup *iface) ++{ ++ struct cifgroup *This = impl_from_ICifGroup(iface); ++ ++ FIXME("(%p): stub\n", This); ++ ++ return 0; ++} ++ ++static const ICifGroupVtbl cifgroupVtbl = ++{ ++ group_GetID, ++ group_GetDescription, ++ group_GetPriority, ++ group_EnumComponents, ++ group_GetCurrentPriority, ++}; ++ ++void component_set_actual_download_size(ICifComponent *iface, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ This->size_actual_download = size; ++} ++ ++void component_set_downloaded(ICifComponent *iface, BOOL value) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ This->downloaded = value; ++} ++ ++void component_set_installed(ICifComponent *iface, BOOL value) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ This->installed = value; ++} ++ ++char *component_get_id(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ return This->id; ++} ++ ++static HRESULT WINAPI component_GetID(ICifComponent *iface, char *id, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, id, size); ++ ++ return copy_substring_null(id, size, This->id); ++} ++ ++static HRESULT WINAPI component_GetGUID(ICifComponent *iface, char *guid, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, guid, size); ++ ++ return copy_substring_null(guid, size, This->guid); ++} ++ ++static HRESULT WINAPI component_GetDescription(ICifComponent *iface, char *desc, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, desc, size); ++ ++ return copy_substring_null(desc, size, This->description); ++} ++ ++static HRESULT WINAPI component_GetDetails(ICifComponent *iface, char *details, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, details, size); ++ ++ return copy_substring_null(details, size, This->details); ++} ++ ++static HRESULT WINAPI component_GetUrl(ICifComponent *iface, UINT index, char *url, DWORD size, DWORD *flags) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ struct url_info *entry; ++ ++ TRACE("(%p)->(%u, %p, %lu, %p)\n", This, index, url, size, flags); ++ ++ /* FIXME: check how functions behaves for url == NULL */ ++ ++ if (!flags) ++ return E_FAIL; ++ ++ LIST_FOR_EACH_ENTRY(entry, &This->urls, struct url_info, entry) ++ { ++ if (entry->index != index) ++ continue; ++ ++ *flags = entry->flags; ++ return copy_substring_null(url, size, entry->url); ++ } ++ ++ return E_FAIL; ++} ++ ++static HRESULT WINAPI component_GetFileExtractList(ICifComponent *iface, UINT index, char *list, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%u, %p, %lu): stub\n", This, index, list, size); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetUrlCheckRange(ICifComponent *iface, UINT index, DWORD *min, DWORD *max) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%u, %p, %p): stub\n", This, index, min, max); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetCommand(ICifComponent *iface, UINT index, char *cmd, DWORD cmd_size, char *switches, DWORD switch_size, DWORD *type) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%u, %p, %lu, %p, %lu, %p): stub\n", This, index, cmd, cmd_size, switches, switch_size, type); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetVersion(ICifComponent *iface, DWORD *version, DWORD *build) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %p)\n", This, version, build); ++ ++ if (!version || !build) ++ return E_FAIL; ++ ++ *version = This->version; ++ *build = This->build; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI component_GetLocale(ICifComponent *iface, char *locale, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, locale, size); ++ ++ return copy_substring_null(locale, size, This->locale); ++} ++ ++static HRESULT WINAPI component_GetUninstallKey(ICifComponent *iface, char *key, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, key, size); ++ ++ return copy_substring_null(key, size, This->key_uninstall); ++} ++ ++static HRESULT WINAPI component_GetInstalledSize(ICifComponent *iface, DWORD *win, DWORD *app) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %p)\n", This, win, app); ++ ++ if (!win || !app) ++ return E_FAIL; ++ ++ *win = This->size_win; ++ *app = This->size_app; ++ ++ return S_OK; ++} ++ ++static DWORD WINAPI component_GetDownloadSize(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->size_download; ++} ++ ++static DWORD WINAPI component_GetExtractSize(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->size_extracted; ++} ++ ++static HRESULT WINAPI component_GetSuccessKey(ICifComponent *iface, char *key, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, key, size); ++ ++ return copy_substring_null(key, size, This->key_success); ++} ++ ++static HRESULT WINAPI component_GetProgressKeys(ICifComponent *iface, char *progress, DWORD progress_size, ++ char *cancel, DWORD cancel_size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ HRESULT hr; ++ ++ TRACE("(%p)->(%p, %lu, %p, %lu): semi-stub\n", This, progress, progress_size, cancel, cancel_size); ++ ++ hr = copy_substring_null(progress, progress_size, This->key_progress); ++ if (hr != S_OK) return hr; ++ ++ if (cancel_size > 0 && cancel) ++ *cancel = 0; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI component_IsActiveSetupAware(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->as_aware ? S_OK : S_FALSE; ++} ++ ++static HRESULT WINAPI component_IsRebootRequired(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->reboot ? S_OK : S_FALSE; ++} ++ ++static HRESULT WINAPI component_RequiresAdminRights(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->admin ? S_OK : S_FALSE; ++} ++ ++static DWORD WINAPI component_GetPriority(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->priority; ++} ++ ++static HRESULT WINAPI component_GetDependency(ICifComponent *iface, UINT index, char *id, DWORD id_size, char *type, DWORD *ver, DWORD *build) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ struct dependency_info *entry; ++ ICifComponent *dependency; ++ int pos = 0; ++ ++ TRACE("(%p)->(%u, %p, %lu, %p, %p, %p)\n", This, index, id, id_size, type, ver, build); ++ ++ if (!id || !ver || !build) ++ return E_FAIL; ++ ++ LIST_FOR_EACH_ENTRY(entry, &This->dependencies, struct dependency_info, entry) ++ { ++ if (pos++ < index) ++ continue; ++ ++ if (ICifFile_FindComponent(This->parent, entry->id, &dependency) == S_OK) ++ { ++ ICifComponent_GetVersion(dependency, ver, build); ++ } ++ else ++ { ++ *ver = -1; ++ *build = -1; ++ } ++ ++ if (entry->type) ++ *type = *entry->type; ++ else ++ *type = 'I'; ++ ++ return copy_substring_null(id, id_size, entry->id); ++ } ++ ++ return E_FAIL; ++} ++ ++static DWORD WINAPI component_GetPlatform(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->platform; ++} ++ ++static HRESULT WINAPI component_GetMode(ICifComponent *iface, UINT index, char *mode, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%u, %p, %lu): stub\n", This, index, mode, size); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetGroup(ICifComponent *iface, char *id, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, id, size); ++ ++ return copy_substring_null(id, size, This->group); ++} ++ ++static HRESULT WINAPI component_IsUIVisible(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->visibleui ? S_OK : S_FALSE; ++} ++ ++static HRESULT WINAPI component_GetPatchID(ICifComponent *iface, char *id, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, id, size); ++ ++ return copy_substring_null(id, size, This->patchid); ++} ++ ++static HRESULT WINAPI component_GetDetVersion(ICifComponent *iface, char *dll, DWORD dll_size, char *entry, DWORD entry_size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%p, %lu, %p, %lu): stub\n", This, dll, dll_size, entry, entry_size); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetTreatAsOneComponents(ICifComponent *iface, UINT index, char *id, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%u, %p, %lu): stub\n", This, index, id, size); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI component_GetCustomData(ICifComponent *iface, char *key, char *data, DWORD size) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%s, %p, %lu): stub\n", This, debugstr_a(key), data, size); ++ ++ return E_NOTIMPL; ++} ++ ++static DWORD WINAPI component_IsComponentInstalled(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->installed; ++} ++ ++static HRESULT WINAPI component_IsComponentDownloaded(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->downloaded ? S_OK : S_FALSE; ++} ++ ++static DWORD WINAPI component_IsThisVersionInstalled(ICifComponent *iface, DWORD version, DWORD build, DWORD *ret_version, DWORD *ret_build) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ FIXME("(%p)->(%lu, %lu, %p, %p): stub\n", This, version, build, ret_version, ret_build); ++ ++ return 0; ++} ++ ++static DWORD WINAPI component_GetInstallQueueState(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->queue_state; ++} ++ ++static HRESULT WINAPI component_SetInstallQueueState(ICifComponent *iface, DWORD state) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%lu)\n", This, state); ++ ++ This->queue_state = state; ++ return S_OK; ++} ++ ++static DWORD WINAPI component_GetActualDownloadSize(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->size_download; ++} ++ ++static DWORD WINAPI component_GetCurrentPriority(ICifComponent *iface) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return This->current_priority; ++} ++ ++ ++static HRESULT WINAPI component_SetCurrentPriority(ICifComponent *iface, DWORD priority) ++{ ++ struct cifcomponent *This = impl_from_ICifComponent(iface); ++ ++ TRACE("(%p)->(%lu)\n", This, priority); ++ ++ This->current_priority = priority; ++ return S_OK; ++} ++ ++static const ICifComponentVtbl cifcomponentVtbl = ++{ ++ component_GetID, ++ component_GetGUID, ++ component_GetDescription, ++ component_GetDetails, ++ component_GetUrl, ++ component_GetFileExtractList, ++ component_GetUrlCheckRange, ++ component_GetCommand, ++ component_GetVersion, ++ component_GetLocale, ++ component_GetUninstallKey, ++ component_GetInstalledSize, ++ component_GetDownloadSize, ++ component_GetExtractSize, ++ component_GetSuccessKey, ++ component_GetProgressKeys, ++ component_IsActiveSetupAware, ++ component_IsRebootRequired, ++ component_RequiresAdminRights, ++ component_GetPriority, ++ component_GetDependency, ++ component_GetPlatform, ++ component_GetMode, ++ component_GetGroup, ++ component_IsUIVisible, ++ component_GetPatchID, ++ component_GetDetVersion, ++ component_GetTreatAsOneComponents, ++ component_GetCustomData, ++ component_IsComponentInstalled, ++ component_IsComponentDownloaded, ++ component_IsThisVersionInstalled, ++ component_GetInstallQueueState, ++ component_SetInstallQueueState, ++ component_GetActualDownloadSize, ++ component_GetCurrentPriority, ++ component_SetCurrentPriority, ++}; ++ ++static HRESULT WINAPI enum_components_QueryInterface(IEnumCifComponents *iface, REFIID riid, void **ppv) ++{ ++ struct ciffenum_components *This = impl_from_IEnumCifComponents(iface); ++ ++ if (IsEqualGUID(&IID_IUnknown, riid)) ++ { ++ TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); ++ *ppv = &This->IEnumCifComponents_iface; ++ } ++ /* ++ else if (IsEqualGUID(&IID_IEnumCifComponents, riid)) ++ { ++ TRACE("(%p)->(IID_ICifFile %p)\n", This, ppv); ++ *ppv = &This->IEnumCifComponents_iface; ++ } ++ */ ++ else ++ { ++ FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); ++ *ppv = NULL; ++ return E_NOINTERFACE; ++ } ++ ++ IUnknown_AddRef((IUnknown *)*ppv); ++ return S_OK; ++} ++ ++static ULONG WINAPI enum_components_AddRef(IEnumCifComponents *iface) ++{ ++ struct ciffenum_components *This = impl_from_IEnumCifComponents(iface); ++ LONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ return ref; ++} ++ ++static ULONG WINAPI enum_components_Release(IEnumCifComponents *iface) ++{ ++ struct ciffenum_components *This = impl_from_IEnumCifComponents(iface); ++ LONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ if(!ref) ++ { ++ ICifFile_Release(This->file); ++ heap_free(This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI enum_components_Next(IEnumCifComponents *iface, ICifComponent **component) ++{ ++ struct ciffenum_components *This = impl_from_IEnumCifComponents(iface); ++ struct cifcomponent *comp; ++ ++ TRACE("(%p)->(%p)\n", This, component); ++ ++ if (!component) ++ return E_FAIL; ++ ++ if (!This->position) ++ { ++ *component = NULL; ++ return E_FAIL; ++ } ++ ++ do ++ { ++ This->position = list_next(This->start, This->position); ++ if (!This->position) ++ { ++ *component = NULL; ++ return E_FAIL; ++ } ++ ++ comp = CONTAINING_RECORD(This->position, struct cifcomponent, entry); ++ } while (This->group_id && (!comp->group || strcmp(This->group_id, comp->group))); ++ ++ *component = &comp->ICifComponent_iface; ++ return S_OK; ++} ++ ++static HRESULT WINAPI enum_components_Reset(IEnumCifComponents *iface) ++{ ++ struct ciffenum_components *This = impl_from_IEnumCifComponents(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ This->position = This->start; ++ return S_OK; ++} ++ ++static const IEnumCifComponentsVtbl enum_componentsVtbl = ++{ ++ enum_components_QueryInterface, ++ enum_components_AddRef, ++ enum_components_Release, ++ enum_components_Next, ++ enum_components_Reset, ++}; ++ ++static HRESULT enum_components_create(ICifFile *file, struct list *start, char *group_id, IEnumCifComponents **iface) ++{ ++ struct ciffenum_components *enumerator; ++ ++ enumerator = heap_alloc_zero(sizeof(*enumerator)); ++ if (!enumerator) return E_OUTOFMEMORY; ++ ++ enumerator->IEnumCifComponents_iface.lpVtbl = &enum_componentsVtbl; ++ enumerator->ref = 1; ++ enumerator->file = file; ++ enumerator->start = start; ++ enumerator->position = start; ++ enumerator->group_id = group_id; ++ ++ ICifFile_AddRef(file); ++ ++ *iface = &enumerator->IEnumCifComponents_iface; ++ return S_OK; ++} ++ ++static HRESULT WINAPI enum_groups_QueryInterface(IEnumCifGroups *iface, REFIID riid, void **ppv) ++{ ++ struct ciffenum_groups *This = impl_from_IEnumCifGroups(iface); ++ ++ if (IsEqualGUID(&IID_IUnknown, riid)) ++ { ++ TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); ++ *ppv = &This->IEnumCifGroups_iface; ++ } ++ /* ++ else if (IsEqualGUID(&IID_IEnumCifGroups, riid)) ++ { ++ TRACE("(%p)->(IID_ICifFile %p)\n", This, ppv); ++ *ppv = &This->IEnumCifGroups_iface; ++ } ++ */ ++ else ++ { ++ FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); ++ *ppv = NULL; ++ return E_NOINTERFACE; ++ } ++ ++ IUnknown_AddRef((IUnknown *)*ppv); ++ return S_OK; ++} ++ ++static ULONG WINAPI enum_groups_AddRef(IEnumCifGroups *iface) ++{ ++ struct ciffenum_groups *This = impl_from_IEnumCifGroups(iface); ++ LONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ return ref; ++} ++ ++static ULONG WINAPI enum_groups_Release(IEnumCifGroups *iface) ++{ ++ struct ciffenum_groups *This = impl_from_IEnumCifGroups(iface); ++ LONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ if(!ref) ++ { ++ ICifFile_Release(This->file); ++ heap_free(This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI enum_groups_Next(IEnumCifGroups *iface, ICifGroup **group) ++{ ++ struct ciffenum_groups *This = impl_from_IEnumCifGroups(iface); ++ struct cifgroup *gp; ++ ++ TRACE("(%p)->(%p)\n", This, group); ++ ++ if (!This->position || !group) ++ return E_FAIL; ++ ++ This->position = list_next(This->start, This->position); ++ ++ if (!This->position) ++ return E_FAIL; ++ ++ gp = CONTAINING_RECORD(This->position, struct cifgroup, entry); ++ *group = &gp->ICifGroup_iface; ++ return S_OK; ++} ++ ++static HRESULT WINAPI enum_groups_Reset(IEnumCifGroups *iface) ++{ ++ struct ciffenum_groups *This = impl_from_IEnumCifGroups(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ This->position = This->start; ++ return S_OK; ++} ++ ++static const IEnumCifGroupsVtbl enum_groupsVtbl = ++{ ++ enum_groups_QueryInterface, ++ enum_groups_AddRef, ++ enum_groups_Release, ++ enum_groups_Next, ++ enum_groups_Reset, ++}; ++ ++static HRESULT enum_groups_create(ICifFile *file, struct list *start, IEnumCifGroups **iface) ++{ ++ struct ciffenum_groups *enumerator; ++ ++ enumerator = heap_alloc_zero(sizeof(*enumerator)); ++ if (!enumerator) return E_OUTOFMEMORY; ++ ++ enumerator->IEnumCifGroups_iface.lpVtbl = &enum_groupsVtbl; ++ enumerator->ref = 1; ++ enumerator->file = file; ++ enumerator->start = start; ++ enumerator->position = start; ++ ++ ICifFile_AddRef(file); ++ ++ *iface = &enumerator->IEnumCifGroups_iface; ++ return S_OK; ++} ++ ++static HRESULT WINAPI ciffile_QueryInterface(ICifFile *iface, REFIID riid, void **ppv) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ if (IsEqualGUID(&IID_IUnknown, riid)) ++ { ++ TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); ++ *ppv = &This->ICifFile_iface; ++ } ++ else if (IsEqualGUID(&IID_ICifFile, riid)) ++ { ++ TRACE("(%p)->(IID_ICifFile %p)\n", This, ppv); ++ *ppv = &This->ICifFile_iface; ++ } ++ else ++ { ++ FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); ++ *ppv = NULL; ++ return E_NOINTERFACE; ++ } ++ ++ IUnknown_AddRef((IUnknown *)*ppv); ++ return S_OK; ++} ++ ++static ULONG WINAPI ciffile_AddRef(ICifFile *iface) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ LONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ return ref; ++} ++ ++static ULONG WINAPI ciffile_Release(ICifFile *iface) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ LONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("(%p) ref=%ld\n", This, ref); ++ ++ if(!ref) ++ { ++ struct cifcomponent *comp, *comp_next; ++ struct cifgroup *group, *group_next; ++ ++ heap_free(This->name); ++ ++ LIST_FOR_EACH_ENTRY_SAFE(comp, comp_next, &This->components, struct cifcomponent, entry) ++ { ++ list_remove(&comp->entry); ++ component_free(comp); ++ } ++ ++ LIST_FOR_EACH_ENTRY_SAFE(group, group_next, &This->groups, struct cifgroup, entry) ++ { ++ list_remove(&group->entry); ++ group_free(group); ++ } ++ ++ heap_free(This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI ciffile_EnumComponents(ICifFile *iface, IEnumCifComponents **enum_components, DWORD filter, void *pv) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ TRACE("(%p)->(%p, %#lx, %p)\n", This, enum_components, filter, pv); ++ ++ if (filter) ++ FIXME("filter (%#lx) not supported\n", filter); ++ if (pv) ++ FIXME("how to handle pv (%p)?\n", pv); ++ ++ return enum_components_create(iface, &This->components, NULL, enum_components); ++} ++ ++static HRESULT WINAPI ciffile_FindComponent(ICifFile *iface, const char *id, ICifComponent **component) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ struct cifcomponent *comp; ++ ++ TRACE("(%p)->(%s, %p)\n", This, debugstr_a(id), component); ++ ++ LIST_FOR_EACH_ENTRY(comp, &This->components, struct cifcomponent, entry) ++ { ++ if (strcmp(comp->id, id) != 0) ++ continue; ++ ++ *component = &comp->ICifComponent_iface; ++ return S_OK; ++ } ++ ++ return E_FAIL; ++} ++ ++static HRESULT WINAPI ciffile_EnumGroups(ICifFile *iface, IEnumCifGroups **enum_groups, DWORD filter, void *pv) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ TRACE("(%p)->(%p, %#lx, %p)\n", This, enum_groups, filter, pv); ++ ++ if (filter) ++ FIXME("filter (%#lx) not supported\n", filter); ++ if (pv) ++ FIXME("how to handle pv (%p)?\n", pv); ++ ++ return enum_groups_create(iface, &This->groups, enum_groups); ++} ++ ++static HRESULT WINAPI ciffile_FindGroup(ICifFile *iface, const char *id, ICifGroup **group) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ struct cifgroup *gp; ++ ++ TRACE("(%p)->(%s, %p)\n", This, debugstr_a(id), group); ++ ++ LIST_FOR_EACH_ENTRY(gp, &This->groups, struct cifgroup, entry) ++ { ++ if (strcmp(gp->id, id) != 0) ++ continue; ++ ++ *group = &gp->ICifGroup_iface; ++ return S_OK; ++ } ++ ++ return E_FAIL; ++} ++ ++static HRESULT WINAPI ciffile_EnumModes(ICifFile *iface, IEnumCifModes **cuf_modes, DWORD filter, void *pv) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ FIXME("(%p)->(%p, %lu, %p): stub\n", This, cuf_modes, filter, pv); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI ciffile_FindMode(ICifFile *iface, const char *id, ICifMode **mode) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ FIXME("(%p)->(%s, %p): stub\n", This, debugstr_a(id), mode); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI ciffile_GetDescription(ICifFile *iface, char *desc, DWORD size) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ TRACE("(%p)->(%p, %lu)\n", This, desc, size); ++ ++ return copy_substring_null(desc, size, This->name); ++} ++ ++static HRESULT WINAPI ciffile_GetDetDlls(ICifFile *iface, char *dlls, DWORD size) ++{ ++ struct ciffile *This = impl_from_ICiffile(iface); ++ ++ FIXME("(%p)->(%p, %lu): stub\n", This, dlls, size); ++ ++ return E_NOTIMPL; ++} ++ ++static const ICifFileVtbl ciffileVtbl = ++{ ++ ciffile_QueryInterface, ++ ciffile_AddRef, ++ ciffile_Release, ++ ciffile_EnumComponents, ++ ciffile_FindComponent, ++ ciffile_EnumGroups, ++ ciffile_FindGroup, ++ ciffile_EnumModes, ++ ciffile_FindMode, ++ ciffile_GetDescription, ++ ciffile_GetDetDlls, ++}; ++ ++static BOOL copy_string(char **dest, const char *source) ++{ ++ if (!source) ++ { ++ *dest = NULL; ++ return TRUE; ++ } ++ ++ *dest = strdupA(source); ++ if (!dest) return FALSE; ++ return TRUE; ++} ++ ++static BOOL section_get_str(struct inf_section *inf_sec, const char *key, char **value, const char *def) ++{ ++ struct inf_value *inf_val; ++ ++ inf_val = inf_get_value(inf_sec, key); ++ if (!inf_val) return copy_string(value, def); ++ ++ *value = inf_value_get_value(inf_val); ++ if (!*value) return FALSE; ++ ++ return TRUE; ++} ++ ++static char *next_part(char **str, BOOL strip_quotes) ++{ ++ char *start = *str; ++ char *next = *str; ++ ++ while (*next && *next != ',') ++ next++; ++ ++ if (!*next) ++ { ++ *str = trim(start, NULL, strip_quotes); ++ return NULL; ++ } ++ ++ *next = 0; ++ *str = trim(start, NULL, strip_quotes); ++ return ++next; ++} ++ ++static BOOL value_get_str_field(struct inf_value *inf_val, int field, char **value, const char *def) ++{ ++ char *line, *str, *next; ++ int i = 0; ++ ++ line = inf_value_get_value(inf_val); ++ if (!line) return FALSE; ++ ++ str = line; ++ do ++ { ++ i++; ++ next = next_part(&str, TRUE); ++ ++ if (field == i) ++ { ++ BOOL ret = copy_string(value, str); ++ heap_free(line); ++ return ret; ++ } ++ ++ str = next; ++ } while (str); ++ ++ return copy_string(value, def); ++} ++ ++/* ++static BOOL section_get_str_field(struct inf_section *inf_sec, const char *key, int field, char **value, const char *def) ++{ ++ struct inf_value *inf_val; ++ ++ inf_val = inf_get_value(inf_sec, key); ++ if (!inf_val) return copy_string(value, def); ++ ++ return value_get_str_field(inf_val, field, value, def); ++} ++*/ ++ ++static BOOL section_get_dword(struct inf_section *inf_sec, const char *key, DWORD *value, DWORD def) ++{ ++ struct inf_value *inf_val; ++ char *str; ++ ++ inf_val = inf_get_value(inf_sec, key); ++ if (!inf_val) ++ { ++ *value = def; ++ return TRUE; ++ } ++ ++ str = inf_value_get_value(inf_val); ++ if (!str) return FALSE; ++ ++ *value = atoi(str); ++ heap_free(str); ++ ++ return TRUE; ++} ++ ++static BOOL value_get_dword_field(struct inf_value *inf_val, int field, DWORD *value, DWORD def) ++{ ++ char *value_str; ++ BOOL ret; ++ ++ ret = value_get_str_field(inf_val, field, &value_str, NULL); ++ if (!ret) return FALSE; ++ if (!value_str) ++ { ++ *value = def; ++ return TRUE; ++ } ++ ++ *value = atoi(value_str); ++ heap_free(value_str); ++ ++ return TRUE; ++} ++ ++static BOOL section_get_dword_field(struct inf_section *inf_sec, const char *key, int field, DWORD *value, DWORD def) ++{ ++ struct inf_value *inf_val; ++ ++ inf_val = inf_get_value(inf_sec, key); ++ if (!inf_val) ++ { ++ *value = def; ++ return TRUE; ++ } ++ ++ return value_get_dword_field(inf_val, field, value, def); ++} ++ ++static HRESULT process_version(struct ciffile *file, struct inf_section *section) ++{ ++ if (!section_get_str(section, "DisplayName", &file->name, DEFAULT_INSTALLER_DESC)) ++ return E_OUTOFMEMORY; ++ ++ return S_OK; ++} ++ ++static BOOL read_version_entry(struct inf_section *section, DWORD *ret_ver, DWORD *ret_build) ++{ ++ DWORD version = 0; ++ DWORD build = 0; ++ char *line, *str, *next; ++ ++ if (!section_get_str(section, "Version", &line, NULL)) ++ return FALSE; ++ if (!line) goto done; ++ ++ str = line; ++ ++ next = next_part(&str, TRUE); ++ version |= atoi(str) << 16; ++ if (!next) goto done; ++ str = next; ++ ++ next = next_part(&str, TRUE); ++ version |= atoi(str) & 0xffff; ++ if (!next) goto done; ++ str = next; ++ ++ next = next_part(&str, TRUE); ++ build |= atoi(str) << 16; ++ if (!next) goto done; ++ str = next; ++ ++ next_part(&str, TRUE); ++ build |= atoi(str) & 0xffff; ++ ++done: ++ heap_free(line); ++ *ret_ver = version; ++ *ret_build = build; ++ return TRUE; ++} ++ ++static BOOL read_platform_entry(struct inf_section *section, DWORD *ret_platform) ++{ ++ DWORD platform = PLATFORM_ALL; ++ char *line, *str, *next; ++ ++ if (!section_get_str(section, "Platform", &line, NULL)) ++ return FALSE; ++ if (!line) goto done; ++ ++ platform = 0; ++ str = line; ++ do ++ { ++ next = next_part(&str, TRUE); ++ ++ if (strcasecmp(str, "Win95") == 0) ++ platform |= PLATFORM_WIN98; ++ else if (strcasecmp(str, "Win98") == 0) ++ platform |= PLATFORM_WIN98; ++ else if (strcasecmp(str, "NT4") == 0) ++ platform |= PLATFORM_NT4; ++ else if (strcasecmp(str, "NT5") == 0) ++ platform |= PLATFORM_NT5; ++ else if (strcasecmp(str, "NT4Alpha") == 0) ++ platform |= PLATFORM_NT4; ++ else if (strcasecmp(str, "NT5Alpha") == 0) ++ platform |= PLATFORM_NT5; ++ else if (strcasecmp(str, "Millen") == 0) ++ platform |= PLATFORM_MILLEN; ++ else ++ FIXME("Unknown platform: %s\n", debugstr_a(str)); ++ ++ str = next; ++ } while (str); ++ ++done: ++ heap_free(line); ++ *ret_platform = platform; ++ return TRUE; ++} ++ ++static BOOL read_dependencies(struct cifcomponent *component, struct inf_section *section) ++{ ++ struct dependency_info *dependency; ++ char *line, *str, *next; ++ BOOL ret = TRUE; ++ ++ if (!section_get_str(section, "Dependencies", &line, NULL)) ++ return E_OUTOFMEMORY; ++ if (!line) goto done; ++ ++ ret = FALSE; ++ str = line; ++ do ++ { ++ next = next_part(&str, TRUE); ++ ++ dependency = heap_alloc_zero(sizeof(*dependency)); ++ if (!dependency) goto done; ++ ++ dependency->id = strdupA(str); ++ if (!dependency->id) ++ { ++ heap_free(dependency); ++ goto done; ++ } ++ ++ dependency->type = strstr(dependency->id, ":"); ++ if (dependency->type) *dependency->type++ = 0; ++ ++ list_add_tail(&component->dependencies, &dependency->entry); ++ ++ str = next; ++ } while (str); ++ ++ ret = TRUE; ++ ++done: ++ heap_free(line); ++ return ret; ++} ++ ++static BOOL read_urls(struct cifcomponent *component, struct inf_section *section) ++{ ++ struct inf_value *inf_value = NULL; ++ struct url_info *url_entry; ++ char *str, *next; ++ int index; ++ ++ while (inf_section_next_value(section, &inf_value)) ++ { ++ str = inf_value_get_key(inf_value); ++ if (!str) return E_OUTOFMEMORY; ++ ++ if (strncasecmp(str, "URL", 3)) ++ goto next; ++ ++ if (!str[3]) ++ goto next; ++ ++ index = strtol(str+3, &next, 10); ++ if (next == str+3 || *next != 0 || index < 1) ++ goto next; ++ index--; ++ ++ url_entry = heap_alloc_zero(sizeof(*url_entry)); ++ if (!url_entry) goto error; ++ ++ url_entry->index = index; ++ ++ if (!value_get_str_field(inf_value, 1, &url_entry->url, NULL)) ++ goto error; ++ if (!url_entry->url || !*url_entry->url) ++ { ++ url_entry_free(url_entry); ++ goto next; ++ } ++ ++ if (!value_get_dword_field(inf_value, 2, &url_entry->flags, 0)) ++ goto error; ++ ++ list_add_tail(&component->urls, &url_entry->entry); ++ ++ next: ++ heap_free(str); ++ } ++ ++ return TRUE; ++ ++error: ++ heap_free(str); ++ url_entry_free(url_entry); ++ return FALSE; ++}; ++ ++void add_component_by_priority(struct ciffile *file, struct cifcomponent *component) ++{ ++ struct cifcomponent *entry; ++ ++ LIST_FOR_EACH_ENTRY(entry, &file->components, struct cifcomponent, entry) ++ { ++ if (entry->priority > component->priority) ++ continue; ++ ++ list_add_before(&entry->entry, &component->entry); ++ return; ++ } ++ ++ list_add_tail(&file->components, &component->entry); ++} ++ ++static HRESULT process_component(struct ciffile *file, struct inf_section *section, const char *section_name) ++{ ++ struct cifcomponent *component; ++ HRESULT hr = E_OUTOFMEMORY; ++ ++ component = heap_alloc_zero(sizeof(*component)); ++ if (!component) return E_OUTOFMEMORY; ++ ++ component->ICifComponent_iface.lpVtbl = &cifcomponentVtbl; ++ component->parent = &file->ICifFile_iface; ++ ++ list_init(&component->urls); ++ list_init(&component->dependencies); ++ ++ component->queue_state = ActionNone; ++ ++ component->id = strdupA(section_name); ++ if (!component->id) goto error; ++ ++ if (!section_get_str(section, "DisplayName", &component->description, NULL)) ++ goto error; ++ if (!section_get_str(section, "GUID", &component->guid, NULL)) ++ goto error; ++ if (!section_get_str(section, "Details", &component->details, NULL)) ++ goto error; ++ if (!section_get_str(section, "Group", &component->group, NULL)) ++ goto error; ++ if (!section_get_str(section, "Locale", &component->locale, "en")) ++ goto error; ++ if (!section_get_str(section, "PatchID", &component->patchid, NULL)) ++ goto error; ++ ++ if (!section_get_dword_field(section, "Size", 1, &component->size_download, 0)) ++ goto error; ++ if (!section_get_dword_field(section, "Size", 2, &component->size_extracted, 0)) ++ goto error; ++ if (!section_get_dword_field(section, "InstalledSize", 1, &component->size_app, 0)) ++ goto error; ++ if (!section_get_dword_field(section, "InstalledSize", 2, &component->size_win, 0)) ++ goto error; ++ ++ if (!section_get_str(section, "SuccessKey", &component->key_success, NULL)) ++ goto error; ++ if (!section_get_str(section, "CancelKey", &component->key_cancel, NULL)) ++ goto error; ++ if (!section_get_str(section, "ProgressKey", &component->key_progress, NULL)) ++ goto error; ++ if (!section_get_str(section, "UninstallKey", &component->key_uninstall, NULL)) ++ goto error; ++ if (!section_get_dword(section, "Reboot", &component->reboot, 0)) ++ goto error; ++ if (!section_get_dword(section, "AdminCheck", &component->admin, 0)) ++ goto error; ++ if (!section_get_dword(section, "UIVisible", &component->visibleui, 1)) ++ goto error; ++ if (!section_get_dword(section, "ActiveSetupAware", &component->as_aware, 0)) ++ goto error; ++ if (!section_get_dword(section, "Priority", &component->priority, 0)) ++ goto error; ++ ++ if (!read_version_entry(section, &component->version, &component->build)) ++ goto error; ++ if (!read_platform_entry(section, &component->platform)) ++ goto error; ++ if (!read_urls(component, section)) ++ goto error; ++ if (!read_dependencies(component, section)) ++ goto error; ++ ++ component->current_priority = component->priority; ++ ++ add_component_by_priority(file, component); ++ return S_OK; ++ ++error: ++ component_free(component); ++ return hr; ++} ++ ++static HRESULT process_group(struct ciffile *file, struct inf_section *section, const char *section_name) ++{ ++ struct cifgroup *group; ++ HRESULT hr = E_OUTOFMEMORY; ++ ++ group = heap_alloc_zero(sizeof(*group)); ++ if (!group) return E_OUTOFMEMORY; ++ ++ group->ICifGroup_iface.lpVtbl = &cifgroupVtbl; ++ group->parent = &file->ICifFile_iface; ++ ++ group->id = strdupA(section_name); ++ if (!group->id) goto error; ++ ++ if (!section_get_str(section, "DisplayName", &group->description, NULL)) ++ goto error; ++ if (!section_get_dword(section, "Priority", &group->priority, 0)) ++ goto error; ++ ++ list_add_head(&file->groups, &group->entry); ++ return S_OK; ++ ++error: ++ group_free(group); ++ return hr; ++} ++ ++static HRESULT process_section(struct ciffile *file, struct inf_section *section, const char *section_name) ++{ ++ HRESULT hr = S_OK; ++ char *type; ++ ++ if (!section_get_str(section, "SectionType", &type, "Component")) ++ return E_OUTOFMEMORY; ++ ++ if (!strcasecmp(type, "Component")) ++ hr = process_component(file, section, section_name); ++ else if (strcasecmp(type, "Group") == 0) ++ hr = process_group(file, section, section_name); ++ else ++ FIXME("Don't know how to process %s\n", debugstr_a(type)); ++ ++ heap_free(type); ++ return hr; ++} ++ ++static HRESULT process_inf(struct ciffile *file, struct inf_file *inf) ++{ ++ struct inf_section *section = NULL; ++ char *section_name; ++ HRESULT hr = S_OK; ++ ++ while (SUCCEEDED(hr) && inf_next_section(inf, §ion)) ++ { ++ section_name = inf_section_get_name(section); ++ if (!section_name) return E_OUTOFMEMORY; ++ ++ TRACE("start processing section %s\n", debugstr_a(section_name)); ++ ++ if (!strcasecmp(section_name, "Strings") || ++ !strncasecmp(section_name, "Strings.", strlen("Strings."))) ++ { ++ /* Ignore string sections */ ++ } ++ else if (strcasecmp(section_name, "Version") == 0) ++ hr = process_version(file, section); ++ else ++ hr = process_section(file, section, section_name); ++ ++ TRACE("Finished processing section %s, hr %#lx.\n", debugstr_a(section_name), hr); ++ heap_free(section_name); ++ } ++ ++ /* In case there was no version section, set the default installer description */ ++ if (SUCCEEDED(hr) && !file->name) ++ { ++ file->name = strdupA(DEFAULT_INSTALLER_DESC); ++ if (!file->name) hr = E_OUTOFMEMORY; ++ } ++ ++ return hr; ++} ++ ++static HRESULT load_ciffile(const char *path, ICifFile **icif) ++{ ++ struct inf_file *inf = NULL; ++ struct ciffile *file; ++ HRESULT hr = E_FAIL; ++ ++ file = heap_alloc_zero(sizeof(*file)); ++ if(!file) return E_OUTOFMEMORY; ++ ++ file->ICifFile_iface.lpVtbl = &ciffileVtbl; ++ file->ref = 1; ++ ++ list_init(&file->components); ++ list_init(&file->groups); ++ ++ hr = inf_load(path, &inf); ++ if (FAILED(hr)) goto error; ++ ++ hr = process_inf(file, inf); ++ if (FAILED(hr)) goto error; ++ ++ *icif = &file->ICifFile_iface; ++ return S_OK; ++ ++error: ++ if (inf) inf_free(inf); ++ ICifFile_Release(&file->ICifFile_iface); ++ return hr; ++} ++ ++HRESULT WINAPI GetICifFileFromFile(ICifFile **icif, const char *path) ++{ ++ TRACE("(%p, %s)\n", icif, debugstr_a(path)); ++ ++ return load_ciffile(path, icif); ++} ++ ++ ++HRESULT WINAPI GetICifRWFileFromFile(ICifRWFile **icif, const char *path) ++{ ++ FIXME("(%p, %s): stub\n", icif, debugstr_a(path)); ++ ++ return E_NOTIMPL; ++} +diff --git a/dlls/inseng/inf.c b/dlls/inseng/inf.c +new file mode 100644 +index 00000000000..bead72c082c +--- /dev/null ++++ b/dlls/inseng/inf.c +@@ -0,0 +1,443 @@ ++/* ++ * Copyright 2016 Michael MĂ¼ller ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "windef.h" ++#include "winbase.h" ++#include "winuser.h" ++ ++#include "inseng_private.h" ++ ++#include "wine/list.h" ++ ++struct inf_value ++{ ++ struct list entry; ++ char *key; ++ char *value; ++ ++ struct inf_section *section; ++}; ++ ++struct inf_section ++{ ++ struct list entry; ++ char *name; ++ struct list values; ++ ++ struct inf_file *file; ++}; ++ ++struct inf_file ++{ ++ char *content; ++ DWORD size; ++ struct list sections; ++}; ++ ++static void inf_value_free(struct inf_value *value) ++{ ++ heap_free(value); ++} ++ ++static void inf_section_free(struct inf_section *section) ++{ ++ struct inf_value *val, *val_next; ++ LIST_FOR_EACH_ENTRY_SAFE(val, val_next, §ion->values, struct inf_value, entry) ++ { ++ list_remove(&val->entry); ++ inf_value_free(val); ++ } ++ ++ heap_free(section); ++} ++ ++static const char *get_substitution(struct inf_file *inf, const char *name, int len) ++{ ++ struct inf_section *sec; ++ struct inf_value *value = NULL; ++ ++ sec = inf_get_section(inf, "Strings"); ++ if (!sec) return NULL; ++ ++ while (inf_section_next_value(sec, &value)) ++ { ++ if (strlen(value->key) == len && !strncasecmp(value->key, name, len)) ++ return value->value; ++ } ++ ++ return NULL; ++} ++ ++static int expand_variables_buffer(struct inf_file *inf, const char *str, char *output) ++{ ++ const char *p, *var_start = NULL; ++ int var_len = 0, len = 0; ++ const char *substitution; ++ ++ for (p = str; *p; p++) ++ { ++ if (*p != '%') ++ { ++ if (var_start) ++ var_len++; ++ else ++ { ++ if (output) ++ *output++ = *p; ++ len++; ++ } ++ ++ continue; ++ } ++ ++ if (!var_start) ++ { ++ var_start = p; ++ var_len = 0; ++ ++ continue; ++ } ++ ++ if (!var_len) ++ { ++ /* just an escaped % */ ++ if (output) ++ *output++ = '%'; ++ len += 1; ++ ++ var_start = NULL; ++ continue; ++ } ++ ++ substitution = get_substitution(inf, var_start + 1, var_len); ++ if (!substitution) ++ { ++ if (output) ++ { ++ memcpy(output, var_start, var_len + 2); ++ output += var_len + 2; ++ } ++ len += var_len + 2; ++ } ++ else ++ { ++ int sub_len = strlen(substitution); ++ ++ if (output) ++ { ++ memcpy(output, substitution, sub_len); ++ output += sub_len; ++ } ++ len += sub_len; ++ } ++ ++ var_start = NULL; ++ } ++ ++ if (output) *output = 0; ++ return len + 1; ++} ++ ++static char *expand_variables(struct inf_file *inf, const char *str) ++{ ++ char *buffer; ++ int len; ++ ++ len = expand_variables_buffer(inf, str, NULL); ++ buffer = heap_alloc(len); ++ if (!len) return NULL; ++ ++ expand_variables_buffer(inf, str, buffer); ++ return buffer; ++} ++ ++void inf_free(struct inf_file *inf) ++{ ++ struct inf_section *sec, *sec_next; ++ LIST_FOR_EACH_ENTRY_SAFE(sec, sec_next, &inf->sections, struct inf_section, entry) ++ { ++ list_remove(&sec->entry); ++ inf_section_free(sec); ++ } ++ ++ heap_free(inf->content); ++ heap_free(inf); ++} ++ ++BOOL inf_next_section(struct inf_file *inf, struct inf_section **sec) ++{ ++ struct list *next_entry, *cur_position; ++ ++ if (*sec) ++ cur_position = &(*sec)->entry; ++ else ++ cur_position = &inf->sections; ++ ++ next_entry = list_next(&inf->sections, cur_position); ++ if (!next_entry) return FALSE; ++ ++ *sec = CONTAINING_RECORD(next_entry, struct inf_section, entry); ++ return TRUE; ++} ++ ++struct inf_section *inf_get_section(struct inf_file *inf, const char *name) ++{ ++ struct inf_section *sec = NULL; ++ ++ while (inf_next_section(inf, &sec)) ++ { ++ if (!strcasecmp(sec->name, name)) ++ return sec; ++ } ++ ++ return NULL; ++} ++ ++char *inf_section_get_name(struct inf_section *section) ++{ ++ return strdupA(section->name); ++} ++ ++BOOL inf_section_next_value(struct inf_section *sec, struct inf_value **value) ++{ ++ struct list *next_entry, *cur_position; ++ ++ if (*value) ++ cur_position = &(*value)->entry; ++ else ++ cur_position = &sec->values; ++ ++ next_entry = list_next(&sec->values, cur_position); ++ if (!next_entry) return FALSE; ++ ++ *value = CONTAINING_RECORD(next_entry, struct inf_value, entry); ++ return TRUE; ++} ++ ++struct inf_value *inf_get_value(struct inf_section *sec, const char *key) ++{ ++ struct inf_value *value = NULL; ++ ++ while (inf_section_next_value(sec, &value)) ++ { ++ if (!strcasecmp(value->key, key)) ++ return value; ++ } ++ ++ return NULL; ++} ++ ++char *inf_value_get_key(struct inf_value *value) ++{ ++ return strdupA(value->key); ++} ++ ++char *inf_value_get_value(struct inf_value *value) ++{ ++ return expand_variables(value->section->file, value->value); ++} ++ ++char *trim(char *str, char **last_chr, BOOL strip_quotes) ++{ ++ char *last; ++ ++ for (; *str; str++) ++ { ++ if (*str != '\t' && *str != ' ') ++ break; ++ } ++ ++ if (!*str) ++ { ++ if (last_chr) *last_chr = str; ++ return str; ++ } ++ ++ last = str + strlen(str) - 1; ++ ++ for (; last > str; last--) ++ { ++ if (*last != '\t' && *last != ' ') ++ break; ++ *last = 0; ++ } ++ ++ if (strip_quotes && last != str) ++ { ++ if (*last == '"' && *str == '"') ++ { ++ str++; ++ *last = 0; ++ } ++ } ++ ++ if (last_chr) *last_chr = last; ++ return str; ++} ++ ++static char *get_next_line(char **str, char **last_chr) ++{ ++ BOOL in_next_line = FALSE; ++ char *start, *next; ++ ++ start = *str; ++ if (!start || !*start) return NULL; ++ ++ for (next = start; *next; next++) ++ { ++ if (*next == '\n' || *next == '\r') ++ { ++ *next = 0; ++ in_next_line = TRUE; ++ } ++ else if (in_next_line) ++ { ++ break; ++ } ++ } ++ ++ *str = next; ++ return trim(start, last_chr, FALSE); ++} ++ ++/* This function only fails in case of an memory allocation error ++ * and does not touch section in case the parsing failed. */ ++static HRESULT inf_section_parse(struct inf_file *inf, char *line, char *last_chr, struct inf_section **section) ++{ ++ struct inf_section *sec; ++ char *comment; ++ char *name; ++ ++ if (*line != '[') ++ return S_OK; ++ ++ line++; ++ ++ comment = strchr(line, ';'); ++ if (comment) ++ { ++ *comment = 0; ++ line = trim(line, &last_chr, FALSE); ++ } ++ ++ if (*last_chr != ']') ++ return S_OK; ++ ++ *last_chr = 0; ++ name = trim(line, NULL, FALSE); ++ if (!name) return S_OK; ++ ++ sec = heap_alloc_zero(sizeof(*sec)); ++ if (!sec) return E_OUTOFMEMORY; ++ ++ sec->name = name; ++ sec->file = inf; ++ list_init(&sec->values); ++ ++ list_add_tail(&inf->sections, &sec->entry); ++ ++ *section = sec; ++ return S_OK; ++} ++ ++static HRESULT inf_value_parse(struct inf_section *sec, char *line) ++{ ++ struct inf_value *key_val; ++ char *key, *value, *del; ++ ++ del = strchr(line, '='); ++ if (!del) return S_OK; ++ ++ *del = 0; ++ key = line; ++ value = del + 1; ++ ++ key = trim(key, NULL, FALSE); ++ value = trim(value, NULL, TRUE); ++ ++ key_val = heap_alloc_zero(sizeof(*key_val)); ++ if (!key_val) return E_OUTOFMEMORY; ++ ++ key_val->key = key; ++ key_val->value = value; ++ key_val->section = sec; ++ ++ list_add_tail(&sec->values, &key_val->entry); ++ return S_OK; ++} ++ ++static HRESULT inf_process_content(struct inf_file *inf) ++{ ++ struct inf_section *section = NULL; ++ char *content = inf->content; ++ char *line, *last_chr; ++ HRESULT hr = S_OK; ++ ++ while (SUCCEEDED(hr) && (line = get_next_line(&content, &last_chr))) ++ { ++ if (*line == '[') ++ hr = inf_section_parse(inf, line, last_chr, §ion); ++ else if (strchr(line, '=') && section) ++ hr = inf_value_parse(section, line); ++ } ++ ++ return hr; ++} ++ ++HRESULT inf_load(const char *path, struct inf_file **inf_file) ++{ ++ LARGE_INTEGER file_size; ++ struct inf_file *inf; ++ HRESULT hr = E_FAIL; ++ HANDLE file; ++ DWORD read; ++ ++ file = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (file == INVALID_HANDLE_VALUE) return E_FAIL; ++ ++ inf = heap_alloc_zero(sizeof(*inf)); ++ if (!inf) goto error; ++ ++ if (!GetFileSizeEx(file, &file_size)) ++ goto error; ++ ++ inf->size = file_size.QuadPart; ++ ++ inf->content = heap_alloc_zero(inf->size); ++ if (!inf->content) goto error; ++ ++ list_init(&inf->sections); ++ ++ if (!ReadFile(file, inf->content, inf->size, &read, NULL) || read != inf->size) ++ goto error; ++ ++ hr = inf_process_content(inf); ++ if (FAILED(hr)) goto error; ++ ++ CloseHandle(file); ++ *inf_file = inf; ++ return S_OK; ++ ++error: ++ if (inf) inf_free(inf); ++ CloseHandle(file); ++ return hr; ++} +diff --git a/dlls/inseng/inseng.spec b/dlls/inseng/inseng.spec +index 82c0b4d5fe1..7ae46fad3a7 100644 +--- a/dlls/inseng/inseng.spec ++++ b/dlls/inseng/inseng.spec +@@ -7,6 +7,6 @@ + @ stdcall -private DllRegisterServer() + @ stdcall -private DllUnregisterServer() + @ stub DownloadFile +-@ stub GetICifFileFromFile +-@ stub GetICifRWFileFromFile ++@ stdcall GetICifFileFromFile(ptr str) ++@ stdcall GetICifRWFileFromFile(ptr str) + @ stub PurgeDownloadDirectory +diff --git a/dlls/inseng/inseng_main.c b/dlls/inseng/inseng_main.c +index c72a12955b8..87d11472ff0 100644 +--- a/dlls/inseng/inseng_main.c ++++ b/dlls/inseng/inseng_main.c +@@ -2,6 +2,7 @@ + * INSENG Implementation + * + * Copyright 2006 Mike McCormack ++ * Copyright 2016 Michael MĂ¼ller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -28,17 +29,68 @@ + #include "winuser.h" + #include "ole2.h" + #include "rpcproxy.h" ++#include "urlmon.h" ++#include "shlwapi.h" + #include "initguid.h" + #include "inseng.h" + ++#include "inseng_private.h" ++ + #include "wine/debug.h" +-#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(inseng); + ++enum thread_operation ++{ ++ OP_DOWNLOAD, ++ OP_INSTALL ++}; ++ ++struct thread_info ++{ ++ DWORD operation; ++ DWORD jobflags; ++ IEnumCifComponents *enum_comp; ++ ++ DWORD download_size; ++ DWORD install_size; ++ ++ DWORD downloaded_kb; ++ ULONGLONG download_start; ++}; ++ + struct InstallEngine { + IInstallEngine2 IInstallEngine2_iface; ++ IInstallEngineTiming IInstallEngineTiming_iface; + LONG ref; ++ ++ IInstallEngineCallback *callback; ++ char *baseurl; ++ char *downloaddir; ++ ICifFile *icif; ++ DWORD status; ++ ++ /* used for the installation thread */ ++ struct thread_info thread; ++}; ++ ++struct downloadcb ++{ ++ IBindStatusCallback IBindStatusCallback_iface; ++ LONG ref; ++ ++ WCHAR *file_name; ++ WCHAR *cache_file; ++ ++ char *id; ++ char *display; ++ ++ DWORD dl_size; ++ DWORD dl_previous_kb; ++ ++ InstallEngine *engine; ++ HANDLE event_done; ++ HRESULT hr; + }; + + static inline InstallEngine *impl_from_IInstallEngine2(IInstallEngine2 *iface) +@@ -46,6 +98,250 @@ static inline InstallEngine *impl_from_IInstallEngine2(IInstallEngine2 *iface) + return CONTAINING_RECORD(iface, InstallEngine, IInstallEngine2_iface); + } + ++static inline struct downloadcb *impl_from_IBindStatusCallback(IBindStatusCallback *iface) ++{ ++ return CONTAINING_RECORD(iface, struct downloadcb, IBindStatusCallback_iface); ++} ++ ++static inline InstallEngine *impl_from_IInstallEngineTiming(IInstallEngineTiming *iface) ++{ ++ return CONTAINING_RECORD(iface, InstallEngine, IInstallEngineTiming_iface); ++} ++ ++static HRESULT WINAPI downloadcb_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ if (IsEqualGUID(&IID_IUnknown, riid)) ++ { ++ TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); ++ *ppv = &This->IBindStatusCallback_iface; ++ } ++ else if (IsEqualGUID(&IID_IBindStatusCallback, riid)) ++ { ++ TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv); ++ *ppv = &This->IBindStatusCallback_iface; ++ } ++ else ++ { ++ FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); ++ *ppv = NULL; ++ return E_NOINTERFACE; ++ } ++ ++ IUnknown_AddRef((IUnknown *)*ppv); ++ return S_OK; ++} ++ ++static ULONG WINAPI downloadcb_AddRef(IBindStatusCallback *iface) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ LONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("(%p) ref = %ld\n", This, ref); ++ ++ return ref; ++} ++ ++static ULONG WINAPI downloadcb_Release(IBindStatusCallback *iface) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ LONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("(%p) ref = %ld\n", This, ref); ++ ++ if (!ref) ++ { ++ heap_free(This->file_name); ++ heap_free(This->cache_file); ++ ++ IInstallEngine2_Release(&This->engine->IInstallEngine2_iface); ++ heap_free(This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI downloadcb_OnStartBinding(IBindStatusCallback *iface, DWORD reserved, IBinding *pbind) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ TRACE("(%p)->(%lu %p)\n", This, reserved, pbind); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI downloadcb_GetPriority(IBindStatusCallback *iface, LONG *priority) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ FIXME("(%p)->(%p): stub\n", This, priority); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI downloadcb_OnLowResource(IBindStatusCallback *iface, DWORD reserved) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ FIXME("(%p)->(%lu): stub\n", This, reserved); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI downloadcb_OnProgress(IBindStatusCallback *iface, ULONG progress, ++ ULONG progress_max, ULONG status, const WCHAR *status_text) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ HRESULT hr = S_OK; ++ ++ TRACE("%p)->(%lu %lu %lu %s)\n", This, progress, progress_max, status, debugstr_w(status_text)); ++ ++ switch(status) ++ { ++ case BINDSTATUS_BEGINDOWNLOADDATA: ++ if (!This->engine->thread.download_start) ++ This->engine->thread.download_start = GetTickCount64(); ++ /* fall-through */ ++ case BINDSTATUS_DOWNLOADINGDATA: ++ case BINDSTATUS_ENDDOWNLOADDATA: ++ This->engine->thread.downloaded_kb = This->dl_previous_kb + progress / 1024; ++ if (This->engine->callback) ++ { ++ hr = IInstallEngineCallback_OnComponentProgress(This->engine->callback, ++ This->id, INSTALLSTATUS_DOWNLOADING, This->display, NULL, progress / 1024, This->dl_size); ++ } ++ break; ++ ++ case BINDSTATUS_CACHEFILENAMEAVAILABLE: ++ This->cache_file = strdupW(status_text); ++ if (!This->cache_file) ++ { ++ ERR("Failed to allocate memory for cache file\n"); ++ hr = E_OUTOFMEMORY; ++ } ++ break; ++ ++ case BINDSTATUS_CONNECTING: ++ case BINDSTATUS_SENDINGREQUEST: ++ case BINDSTATUS_MIMETYPEAVAILABLE: ++ case BINDSTATUS_FINDINGRESOURCE: ++ break; ++ ++ default: ++ FIXME("Unsupported status %lu\n", status); ++ } ++ ++ return hr; ++} ++ ++static HRESULT WINAPI downloadcb_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ TRACE("(%p)->(%#lx %s)\n", This, hresult, debugstr_w(szError)); ++ ++ if (FAILED(hresult)) ++ { ++ This->hr = hresult; ++ goto done; ++ } ++ ++ if (!This->cache_file) ++ { ++ This->hr = E_FAIL; ++ goto done; ++ } ++ ++ if (CopyFileW(This->cache_file, This->file_name, FALSE)) ++ This->hr = S_OK; ++ else ++ { ++ ERR("CopyFile failed: %lu\n", GetLastError()); ++ This->hr = E_FAIL; ++ } ++ ++done: ++ SetEvent(This->event_done); ++ return S_OK; ++} ++ ++static HRESULT WINAPI downloadcb_GetBindInfo(IBindStatusCallback *iface, ++ DWORD *grfBINDF, BINDINFO *pbindinfo) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo); ++ ++ *grfBINDF = BINDF_PULLDATA | BINDF_NEEDFILE; ++ return S_OK; ++} ++ ++static HRESULT WINAPI downloadcb_OnDataAvailable(IBindStatusCallback *iface, ++ DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ TRACE("(%p)->(%#lx %lu %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI downloadcb_OnObjectAvailable(IBindStatusCallback *iface, ++ REFIID riid, IUnknown *punk) ++{ ++ struct downloadcb *This = impl_from_IBindStatusCallback(iface); ++ ++ FIXME("(%p)->(%s %p): stub\n", This, debugstr_guid(riid), punk); ++ ++ return E_NOTIMPL; ++} ++ ++static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = ++{ ++ downloadcb_QueryInterface, ++ downloadcb_AddRef, ++ downloadcb_Release, ++ downloadcb_OnStartBinding, ++ downloadcb_GetPriority, ++ downloadcb_OnLowResource, ++ downloadcb_OnProgress, ++ downloadcb_OnStopBinding, ++ downloadcb_GetBindInfo, ++ downloadcb_OnDataAvailable, ++ downloadcb_OnObjectAvailable ++}; ++ ++static HRESULT downloadcb_create(InstallEngine *engine, HANDLE event, char *file_name, char *id, ++ char *display, DWORD dl_size, struct downloadcb **callback) ++{ ++ struct downloadcb *cb; ++ ++ cb = heap_alloc_zero(sizeof(*cb)); ++ if (!cb) return E_OUTOFMEMORY; ++ ++ cb->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl; ++ cb->ref = 1; ++ cb->hr = E_FAIL; ++ cb->id = id; ++ cb->display = display; ++ cb->engine = engine; ++ cb->dl_size = dl_size; ++ cb->dl_previous_kb = engine->thread.downloaded_kb; ++ cb->event_done = event; ++ cb->file_name = strAtoW(file_name); ++ if (!cb->file_name) ++ { ++ heap_free(cb); ++ return E_OUTOFMEMORY; ++ } ++ ++ IInstallEngine2_AddRef(&engine->IInstallEngine2_iface); ++ ++ *callback = cb; ++ return S_OK; ++} ++ + static HRESULT WINAPI InstallEngine_QueryInterface(IInstallEngine2 *iface, REFIID riid, void **ppv) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +@@ -59,13 +355,16 @@ static HRESULT WINAPI InstallEngine_QueryInterface(IInstallEngine2 *iface, REFII + }else if(IsEqualGUID(&IID_IInstallEngine2, riid)) { + TRACE("(%p)->(IID_IInstallEngine2 %p)\n", This, ppv); + *ppv = &This->IInstallEngine2_iface; ++ }else if(IsEqualGUID(&IID_IInstallEngineTiming, riid)) { ++ TRACE("(%p)->(IID_IInstallEngineTiming %p)\n", This, ppv); ++ *ppv = &This->IInstallEngineTiming_iface; + }else { +- TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); ++ FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + +- IUnknown_AddRef((IUnknown*)*ppv); ++ IUnknown_AddRef((IUnknown *)*ppv); + return S_OK; + } + +@@ -86,181 +385,726 @@ static ULONG WINAPI InstallEngine_Release(IInstallEngine2 *iface) + + TRACE("(%p) ref=%ld\n", This, ref); + +- if(!ref) ++ if (!ref) ++ { ++ if (This->icif) ++ ICifFile_Release(This->icif); ++ ++ heap_free(This->baseurl); ++ heap_free(This->downloaddir); + heap_free(This); ++ } + + return ref; + } + ++static void set_status(InstallEngine *This, DWORD status) ++{ ++ This->status = status; ++ ++ if (This->callback) ++ IInstallEngineCallback_OnEngineStatusChange(This->callback, status, 0); ++} ++ ++static HRESULT calc_sizes(IEnumCifComponents *enum_comp, DWORD operation, DWORD *size_download, DWORD *size_install) ++{ ++ ICifComponent *comp; ++ DWORD download = 0; ++ DWORD install = 0; ++ HRESULT hr; ++ ++ /* FIXME: what about inactive dependencies and how does ++ * INSTALLOPTIONS_FORCEDEPENDENCIES play into this ?*/ ++ ++ hr = IEnumCifComponents_Reset(enum_comp); ++ if (FAILED(hr)) return hr; ++ ++ while (SUCCEEDED(IEnumCifComponents_Next(enum_comp, &comp))) ++ { ++ if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) ++ continue; ++ ++ /* FIXME: handle install options and find out the default options*/ ++ if (operation == OP_DOWNLOAD && ICifComponent_IsComponentDownloaded(comp) == S_FALSE) ++ download = ICifComponent_GetDownloadSize(comp); ++ /* ++ if (operation == OP_INSTALL && ICifComponent_IsComponentInstalled(comp) == S_FALSE) ++ install = ICifComponent_GetInstalledSize(comp); ++ */ ++ } ++ ++ *size_download = download; ++ *size_install = install; ++ ++ return S_OK; ++} ++ ++static HRESULT get_next_component(IEnumCifComponents *enum_comp, DWORD operation, ICifComponent **ret_comp) ++{ ++ ICifComponent *comp; ++ HRESULT hr; ++ ++ hr = IEnumCifComponents_Reset(enum_comp); ++ if (FAILED(hr)) return hr; ++ ++ while (SUCCEEDED(IEnumCifComponents_Next(enum_comp, &comp))) ++ { ++ if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) ++ continue; ++ ++ /* FIXME: handle install options and find out the default options*/ ++ if (operation == OP_DOWNLOAD && ICifComponent_IsComponentDownloaded(comp) != S_FALSE) ++ continue; ++ if (operation == OP_INSTALL && ICifComponent_IsComponentInstalled(comp) != S_FALSE) ++ continue; ++ ++ *ret_comp = comp; ++ return S_OK; ++ } ++ ++ return S_FALSE; ++} ++ ++static HRESULT get_url(ICifComponent *comp, int index, char **url, DWORD *flags) ++{ ++ char *url_temp = NULL; ++ int size = MAX_PATH / 2; ++ HRESULT hr; ++ ++ /* FIXME: should we add an internal get function to prevent this ugly code ? */ ++ ++ /* check if there is an url with such an index */ ++ hr = ICifComponent_GetUrl(comp, index, NULL, 0, flags); ++ if (FAILED(hr)) ++ { ++ *url = NULL; ++ *flags = 0; ++ return S_OK; ++ } ++ ++ do ++ { ++ size *= 2; ++ heap_free(url_temp); ++ url_temp = heap_alloc(size); ++ if (!url_temp) return E_OUTOFMEMORY; ++ ++ hr = ICifComponent_GetUrl(comp, index, url_temp, size, flags); ++ if (FAILED(hr)) ++ { ++ heap_free(url_temp); ++ return hr; ++ } ++ } ++ while (strlen(url_temp) == size-1); ++ ++ *url = url_temp; ++ return S_OK; ++} ++ ++static char *combine_url(char *baseurl, char *url) ++{ ++ int len_base = strlen(baseurl); ++ int len_url = strlen(url); ++ char *combined; ++ ++ combined = heap_alloc(len_base + len_url + 2); ++ if (!combined) return NULL; ++ ++ strcpy(combined, baseurl); ++ if (len_base && combined[len_base-1] != '/') ++ strcat(combined, "/"); ++ strcat(combined, url); ++ ++ return combined; ++} ++ ++static HRESULT generate_moniker(char *baseurl, char *url, DWORD flags, IMoniker **moniker) ++{ ++ WCHAR *urlW; ++ HRESULT hr; ++ ++ if (flags & URLF_RELATIVEURL) ++ { ++ char *combined; ++ if (!baseurl) ++ return E_FAIL; ++ ++ combined = combine_url(baseurl, url); ++ if (!combined) return E_OUTOFMEMORY; ++ ++ urlW = strAtoW(combined); ++ heap_free(combined); ++ if (!urlW) return E_OUTOFMEMORY; ++ } ++ else ++ { ++ urlW = strAtoW(url); ++ if (!urlW) return E_OUTOFMEMORY; ++ } ++ ++ hr = CreateURLMoniker(NULL, urlW, moniker); ++ heap_free(urlW); ++ return hr; ++} ++ ++static char *merge_path(char *path1, char *path2) ++{ ++ int len = strlen(path1) + strlen(path2) + 2; ++ char *combined = heap_alloc(len); ++ ++ if (!combined) return NULL; ++ strcpy(combined, path1); ++ strcat(combined, "\\"); ++ strcat(combined, path2); ++ ++ return combined; ++} ++ ++static HRESULT download_url(InstallEngine *This, char *id, char *display, char *url, DWORD flags, DWORD dl_size) ++{ ++ struct downloadcb *callback = NULL; ++ char *filename = NULL; ++ IUnknown *unk = NULL; ++ IMoniker *mon = NULL; ++ IBindCtx *bindctx = NULL; ++ HANDLE event = NULL; ++ HRESULT hr; ++ ++ if (!This->downloaddir) ++ { ++ WARN("No download directory set\n"); ++ return E_FAIL; ++ } ++ ++ hr = generate_moniker(This->baseurl, url, flags, &mon); ++ if (FAILED(hr)) ++ { ++ FIXME("Failed to create moniker\n"); ++ return hr; ++ } ++ ++ event = CreateEventW(NULL, TRUE, FALSE, NULL); ++ if (!event) ++ { ++ IMoniker_Release(mon); ++ return E_FAIL; ++ } ++ ++ filename = strrchr(url, '/'); ++ if (!filename) filename = url; ++ ++ filename = merge_path(This->downloaddir, filename); ++ if (!filename) ++ { ++ hr = E_OUTOFMEMORY; ++ goto error; ++ } ++ ++ hr = downloadcb_create(This, event, filename, id, display, dl_size, &callback); ++ if (FAILED(hr)) goto error; ++ ++ hr = CreateAsyncBindCtx(0, &callback->IBindStatusCallback_iface, NULL, &bindctx); ++ if(FAILED(hr)) goto error; ++ ++ hr = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); ++ if (FAILED(hr)) goto error; ++ if (unk) IUnknown_Release(unk); ++ ++ heap_free(filename); ++ IMoniker_Release(mon); ++ IBindCtx_Release(bindctx); ++ ++ WaitForSingleObject(event, INFINITE); ++ hr = callback->hr; ++ ++ CloseHandle(event); ++ IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); ++ return hr; ++ ++error: ++ if (mon) IMoniker_Release(mon); ++ if (event) CloseHandle(event); ++ if (callback) IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); ++ if (bindctx) IBindCtx_Release(bindctx); ++ if (filename) heap_free(filename); ++ return hr; ++} ++ ++static HRESULT process_component_dependencies(InstallEngine *This, ICifComponent *comp) ++{ ++ char id[MAX_ID_LENGTH+1], type; ++ DWORD ver, build; ++ HRESULT hr; ++ int i; ++ ++ for (i = 0;; i++) ++ { ++ hr = ICifComponent_GetDependency(comp, i, id, sizeof(id), &type, &ver, &build); ++ if (SUCCEEDED(hr)) ++ FIXME("Can't handle dependencies yet: %s\n", debugstr_a(id)); ++ else ++ break; ++ } ++ ++ return S_OK; ++} ++ ++static HRESULT process_component(InstallEngine *This, ICifComponent *comp) ++{ ++ DWORD size_dl, size_install, phase; ++ char display[MAX_DISPLAYNAME_LENGTH+1]; ++ char id[MAX_ID_LENGTH+1]; ++ HRESULT hr; ++ int i; ++ ++ hr = ICifComponent_GetID(comp, id, sizeof(id)); ++ if (FAILED(hr)) return hr; ++ ++ TRACE("processing component %s\n", debugstr_a(id)); ++ ++ hr = ICifComponent_GetDescription(comp, display, sizeof(display)); ++ if (FAILED(hr)) return hr; ++ ++ size_dl = (This->thread.operation == OP_DOWNLOAD) ? ICifComponent_GetDownloadSize(comp) : 0; ++ size_install = 0; /* (This->thread.operation == OP_INSTALL) ? ICifComponent_GetInstalledSize(comp) : 0; */ ++ ++ if (This->callback) ++ { ++ IInstallEngineCallback_OnStartComponent(This->callback, id, size_dl, size_install, display); ++ IInstallEngineCallback_OnComponentProgress(This->callback, id, INSTALLSTATUS_INITIALIZING, display, NULL, 0, 0); ++ phase = INSTALLSTATUS_INITIALIZING; ++ } ++ ++ hr = process_component_dependencies(This, comp); ++ if (FAILED(hr)) return hr; ++ ++ if (This->thread.operation == OP_DOWNLOAD) ++ { ++ for (i = 0;; i++) ++ { ++ DWORD flags; ++ char *url; ++ ++ phase = INSTALLSTATUS_DOWNLOADING; ++ ++ hr = get_url(comp, i, &url, &flags); ++ if (FAILED(hr)) goto done; ++ if (!url) break; ++ ++ TRACE("processing url %s\n", debugstr_a(url)); ++ ++ hr = download_url(This, id, display, url, flags, size_dl); ++ heap_free(url); ++ if (FAILED(hr)) ++ { ++ DWORD retry = 0; ++ ++ if (This->callback) ++ IInstallEngineCallback_OnEngineProblem(This->callback, ENGINEPROBLEM_DOWNLOADFAIL, &retry); ++ if (!retry) goto done; ++ ++ i--; ++ continue; ++ } ++ ++ phase = INSTALLSTATUS_CHECKINGTRUST; ++ /* FIXME: check trust */ ++ IInstallEngineCallback_OnComponentProgress(This->callback, id, INSTALLSTATUS_CHECKINGTRUST, display, NULL, 0, 0); ++ } ++ ++ component_set_downloaded(comp, TRUE); ++ phase = INSTALLSTATUS_DOWNLOADFINISHED; ++ } ++ else ++ FIXME("Installation not yet implemented\n"); ++ ++done: ++ IInstallEngineCallback_OnStopComponent(This->callback, id, hr, phase, display, 0); ++ return hr; ++} ++ ++DWORD WINAPI thread_installation(LPVOID param) ++{ ++ InstallEngine *This = param; ++ ICifComponent *comp; ++ HRESULT hr; ++ ++ if (This->callback) ++ IInstallEngineCallback_OnStartInstall(This->callback, This->thread.download_size, This->thread.install_size); ++ ++ for (;;) ++ { ++ hr = get_next_component(This->thread.enum_comp, This->thread.operation, &comp); ++ if (FAILED(hr)) break; ++ if (hr == S_FALSE) ++ { ++ hr = S_OK; ++ break; ++ } ++ ++ hr = process_component(This, comp); ++ if (FAILED(hr)) break; ++ } ++ ++ if (This->callback) ++ IInstallEngineCallback_OnStopInstall(This->callback, hr, NULL, 0); ++ ++ IEnumCifComponents_Release(This->thread.enum_comp); ++ IInstallEngine2_Release(&This->IInstallEngine2_iface); ++ ++ set_status(This, ENGINESTATUS_READY); ++ return 0; ++} ++ ++static HRESULT start_installation(InstallEngine *This, DWORD operation, DWORD jobflags) ++{ ++ HANDLE thread; ++ HRESULT hr; ++ ++ This->thread.operation = operation; ++ This->thread.jobflags = jobflags; ++ This->thread.downloaded_kb = 0; ++ This->thread.download_start = 0; ++ ++ /* Windows sends the OnStartInstall event from a different thread, ++ * but OnStartInstall already contains the required download and install size. ++ * The only way to signal an error from the thread is to send an OnStopComponent / ++ * OnStopInstall signal which can only occur after OnStartInstall. We need to ++ * precompute the sizes here to be able inform the application about errors while ++ * calculating the required sizes. */ ++ ++ hr = ICifFile_EnumComponents(This->icif, &This->thread.enum_comp, 0, NULL); ++ if (FAILED(hr)) return hr; ++ ++ hr = calc_sizes(This->thread.enum_comp, operation, &This->thread.download_size, &This->thread.install_size); ++ if (FAILED(hr)) goto error; ++ ++ IInstallEngine2_AddRef(&This->IInstallEngine2_iface); ++ ++ thread = CreateThread(NULL, 0, thread_installation, This, 0, NULL); ++ if (!thread) ++ { ++ IInstallEngine2_Release(&This->IInstallEngine2_iface); ++ hr = E_FAIL; ++ goto error; ++ } ++ ++ CloseHandle(thread); ++ return S_OK; ++ ++error: ++ IEnumCifComponents_Release(This->thread.enum_comp); ++ return hr; ++} ++ + static HRESULT WINAPI InstallEngine_GetEngineStatus(IInstallEngine2 *iface, DWORD *status) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%p)\n", This, status); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%p)\n", This, status); ++ ++ if (!status) ++ return E_FAIL; ++ ++ *status = This->status; ++ return S_OK; + } + + static HRESULT WINAPI InstallEngine_SetCifFile(IInstallEngine2 *iface, const char *cab_name, const char *cif_name) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %s)\n", This, debugstr_a(cab_name), debugstr_a(cif_name)); ++ ++ FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(cab_name), debugstr_a(cif_name)); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_DownloadComponents(IInstallEngine2 *iface, DWORD flags) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%lx)\n", This, flags); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%#lx)\n", This, flags); ++ ++ /* The interface is not really threadsafe on windows, but we can at least prevent multiple installations */ ++ if (InterlockedCompareExchange((LONG *)&This->status, ENGINESTATUS_INSTALLING, ENGINESTATUS_READY) != ENGINESTATUS_READY) ++ return E_FAIL; ++ ++ if (This->callback) ++ IInstallEngineCallback_OnEngineStatusChange(This->callback, ENGINESTATUS_INSTALLING, 0); ++ ++ return start_installation(This, OP_DOWNLOAD, flags); + } + + static HRESULT WINAPI InstallEngine_InstallComponents(IInstallEngine2 *iface, DWORD flags) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%lx)\n", This, flags); ++ ++ FIXME("(%p)->(%#lx): stub\n", This, flags); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_EnumInstallIDs(IInstallEngine2 *iface, UINT index, char **id) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%d %p)\n", This, index, id); ++ ++ FIXME("(%p)->(%u %p): stub\n", This, index, id); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_EnumDownloadIDs(IInstallEngine2 *iface, UINT index, char **id) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%d %p)\n", This, index, id); +- return E_NOTIMPL; ++ IEnumCifComponents *enum_components; ++ ICifComponent *comp; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%u %p)\n", This, index, id); ++ ++ if (!This->icif || !id) ++ return E_FAIL; ++ ++ hr = ICifFile_EnumComponents(This->icif, &enum_components, 0, NULL); ++ if (FAILED(hr)) return hr; ++ ++ for (;;) ++ { ++ hr = IEnumCifComponents_Next(enum_components, &comp); ++ if (FAILED(hr)) goto done; ++ ++ if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) ++ continue; ++ ++ if (ICifComponent_IsComponentDownloaded(comp) != S_FALSE) ++ continue; ++ ++ if (index == 0) ++ { ++ char *id_src = component_get_id(comp); ++ *id = CoTaskMemAlloc(strlen(id_src) + 1); ++ ++ if (*id) ++ strcpy(*id, id_src); ++ else ++ hr = E_OUTOFMEMORY; ++ goto done; ++ } ++ ++ index--; ++ } ++ ++done: ++ IEnumCifComponents_Release(enum_components); ++ return hr; + } + + static HRESULT WINAPI InstallEngine_IsComponentInstalled(IInstallEngine2 *iface, const char *id, DWORD *status) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %p)\n", This, debugstr_a(id), status); ++ ++ FIXME("(%p)->(%s %p): stub\n", This, debugstr_a(id), status); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_RegisterInstallEngineCallback(IInstallEngine2 *iface, IInstallEngineCallback *callback) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%p)\n", This, callback); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%p)\n", This, callback); ++ ++ This->callback = callback; ++ return S_OK; + } + + static HRESULT WINAPI InstallEngine_UnregisterInstallEngineCallback(IInstallEngine2 *iface) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)\n", This); +- return E_NOTIMPL; ++ ++ TRACE("(%p)\n", This); ++ ++ This->callback = NULL; ++ return S_OK; + } + + static HRESULT WINAPI InstallEngine_SetAction(IInstallEngine2 *iface, const char *id, DWORD action, DWORD priority) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %ld %ld)\n", This, debugstr_a(id), action, priority); +- return E_NOTIMPL; ++ ICifComponent *comp; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%s %lu %lu)\n", This, debugstr_a(id), action, priority); ++ ++ if (!This->icif) ++ return E_FAIL; /* FIXME: check error code */ ++ ++ hr = ICifFile_FindComponent(This->icif, id, &comp); ++ if (FAILED(hr)) return hr; ++ ++ hr = ICifComponent_SetInstallQueueState(comp, action); ++ if (FAILED(hr)) return hr; ++ ++ hr = ICifComponent_SetCurrentPriority(comp, priority); ++ return hr; + } + + static HRESULT WINAPI InstallEngine_GetSizes(IInstallEngine2 *iface, const char *id, COMPONENT_SIZES *sizes) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %p)\n", This, debugstr_a(id), sizes); ++ ++ FIXME("(%p)->(%s %p): stub\n", This, debugstr_a(id), sizes); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_LaunchExtraCommand(IInstallEngine2 *iface, const char *inf_name, const char *section) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %s)\n", This, debugstr_a(inf_name), debugstr_a(section)); ++ ++ FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(inf_name), debugstr_a(section)); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_GetDisplayName(IInstallEngine2 *iface, const char *id, const char *name) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s %s)\n", This, debugstr_a(id), debugstr_a(name)); ++ ++ FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(id), debugstr_a(name)); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_SetBaseUrl(IInstallEngine2 *iface, const char *base_name) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s)\n", This, debugstr_a(base_name)); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%s)\n", This, debugstr_a(base_name)); ++ ++ if (This->baseurl) ++ heap_free(This->baseurl); ++ ++ This->baseurl = strdupA(base_name); ++ return This->baseurl ? S_OK : E_OUTOFMEMORY; + } + + static HRESULT WINAPI InstallEngine_SetDownloadDir(IInstallEngine2 *iface, const char *download_dir) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s)\n", This, debugstr_a(download_dir)); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%s)\n", This, debugstr_a(download_dir)); ++ ++ if (This->downloaddir) ++ heap_free(This->downloaddir); ++ ++ This->downloaddir = strdupA(download_dir); ++ return This->downloaddir ? S_OK : E_OUTOFMEMORY; + } + + static HRESULT WINAPI InstallEngine_SetInstallDrive(IInstallEngine2 *iface, char drive) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%c)\n", This, drive); ++ ++ FIXME("(%p)->(%c): stub\n", This, drive); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_SetInstallOptions(IInstallEngine2 *iface, DWORD flags) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%lx)\n", This, flags); ++ ++ FIXME("(%p)->(%#lx): stub\n", This, flags); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_SetHWND(IInstallEngine2 *iface, HWND hwnd) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%p)\n", This, hwnd); ++ ++ FIXME("(%p)->(%p): stub\n", This, hwnd); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_SetIStream(IInstallEngine2 *iface, IStream *stream) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%p)\n", This, stream); ++ ++ FIXME("(%p)->(%p): stub\n", This, stream); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_Abort(IInstallEngine2 *iface, DWORD flags) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%lx)\n", This, flags); ++ ++ FIXME("(%p)->(%#lx): stub\n", This, flags); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_Suspend(IInstallEngine2 *iface) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)\n", This); ++ ++ FIXME("(%p): stub\n", This); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine_Resume(IInstallEngine2 *iface) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)\n", This); ++ ++ FIXME("(%p): stub\n", This); ++ + return E_NOTIMPL; + } + + static HRESULT WINAPI InstallEngine2_SetLocalCif(IInstallEngine2 *iface, const char *cif) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%s)\n", This, debugstr_a(cif)); +- return E_NOTIMPL; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%s)\n", This, debugstr_a(cif)); ++ ++ if (This->icif) ++ ICifFile_Release(This->icif); ++ ++ set_status(This, ENGINESTATUS_LOADING); ++ ++ hr = GetICifFileFromFile(&This->icif, cif); ++ if (SUCCEEDED(hr)) ++ set_status(This, ENGINESTATUS_READY); ++ else ++ { ++ This->icif = NULL; ++ set_status(This, ENGINESTATUS_NOTREADY); ++ } ++ return hr; + } + + static HRESULT WINAPI InstallEngine2_GetICifFile(IInstallEngine2 *iface, ICifFile **cif_file) + { + InstallEngine *This = impl_from_IInstallEngine2(iface); +- FIXME("(%p)->(%p)\n", This, cif_file); +- return E_NOTIMPL; ++ ++ TRACE("(%p)->(%p)\n", This, cif_file); ++ ++ if (!This->icif || !cif_file) ++ return E_FAIL; ++ ++ ICifFile_AddRef(This->icif); ++ *cif_file = This->icif; ++ return S_OK; + } + +-static const IInstallEngine2Vtbl InstallEngine2Vtbl = { ++static const IInstallEngine2Vtbl InstallEngine2Vtbl = ++{ + InstallEngine_QueryInterface, + InstallEngine_AddRef, + InstallEngine_Release, +@@ -290,6 +1134,70 @@ static const IInstallEngine2Vtbl InstallEngine2Vtbl = { + InstallEngine2_GetICifFile + }; + ++static HRESULT WINAPI InstallEngineTiming_QueryInterface(IInstallEngineTiming *iface, REFIID riid, void **ppv) ++{ ++ InstallEngine *This = impl_from_IInstallEngineTiming(iface); ++ return IInstallEngine2_QueryInterface(&This->IInstallEngine2_iface, riid, ppv); ++} ++ ++static ULONG WINAPI InstallEngineTiming_AddRef(IInstallEngineTiming *iface) ++{ ++ InstallEngine *This = impl_from_IInstallEngineTiming(iface); ++ return IInstallEngine2_AddRef(&This->IInstallEngine2_iface); ++} ++ ++static ULONG WINAPI InstallEngineTiming_Release(IInstallEngineTiming *iface) ++{ ++ InstallEngine *This = impl_from_IInstallEngineTiming(iface); ++ return IInstallEngine2_Release(&This->IInstallEngine2_iface); ++} ++ ++static HRESULT WINAPI InstallEngineTiming_GetRates(IInstallEngineTiming *iface, DWORD *download, DWORD *install) ++{ ++ InstallEngine *This = impl_from_IInstallEngineTiming(iface); ++ ++ FIXME("(%p)->(%p, %p): stub\n", This, download, install); ++ ++ *download = 0; ++ *install = 0; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI InstallEngineTiming_GetInstallProgress(IInstallEngineTiming *iface, INSTALLPROGRESS *progress) ++{ ++ InstallEngine *This = impl_from_IInstallEngineTiming(iface); ++ ULONGLONG elapsed; ++ static int once; ++ ++ if (!once) ++ FIXME("(%p)->(%p): semi-stub\n", This, progress); ++ else ++ TRACE("(%p)->(%p): semi-stub\n", This, progress); ++ ++ progress->dwDownloadKBRemaining = max(This->thread.download_size, This->thread.downloaded_kb) - This->thread.downloaded_kb; ++ ++ elapsed = GetTickCount64() - This->thread.download_start; ++ if (This->thread.download_start && This->thread.downloaded_kb && elapsed > 100) ++ progress->dwDownloadSecsRemaining = (progress->dwDownloadKBRemaining * elapsed) / (This->thread.downloaded_kb * 1000); ++ else ++ progress->dwDownloadSecsRemaining = -1; ++ ++ progress->dwInstallKBRemaining = 0; ++ progress->dwInstallSecsRemaining = -1; ++ ++ return S_OK; ++} ++ ++static const IInstallEngineTimingVtbl InstallEngineTimingVtbl = ++{ ++ InstallEngineTiming_QueryInterface, ++ InstallEngineTiming_AddRef, ++ InstallEngineTiming_Release, ++ InstallEngineTiming_GetRates, ++ InstallEngineTiming_GetInstallProgress, ++}; ++ + static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) + { + *ppv = NULL; +@@ -334,12 +1242,14 @@ static HRESULT WINAPI InstallEngineCF_CreateInstance(IClassFactory *iface, IUnkn + + TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv); + +- engine = heap_alloc(sizeof(*engine)); ++ engine = heap_alloc_zero(sizeof(*engine)); + if(!engine) + return E_OUTOFMEMORY; + + engine->IInstallEngine2_iface.lpVtbl = &InstallEngine2Vtbl; ++ engine->IInstallEngineTiming_iface.lpVtbl = &InstallEngineTimingVtbl; + engine->ref = 1; ++ engine->status = ENGINESTATUS_NOTREADY; + + hres = IInstallEngine2_QueryInterface(&engine->IInstallEngine2_iface, riid, ppv); + IInstallEngine2_Release(&engine->IInstallEngine2_iface); +diff --git a/dlls/inseng/inseng_private.h b/dlls/inseng/inseng_private.h +new file mode 100644 +index 00000000000..55d3899808a +--- /dev/null ++++ b/dlls/inseng/inseng_private.h +@@ -0,0 +1,79 @@ ++/* ++ * Copyright 2016 Michael MĂ¼ller ++ * ++ * 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 ++ */ ++ ++#include "windef.h" ++#include "winbase.h" ++#include "winuser.h" ++#include "ole2.h" ++#include "rpcproxy.h" ++#include "inseng.h" ++#include "wine/heap.h" ++ ++ ++static inline char *strdupA(const char *src) ++{ ++ char *dest = heap_alloc(strlen(src) + 1); ++ if (dest) strcpy(dest, src); ++ return dest; ++} ++ ++static inline WCHAR *strdupW(const WCHAR *src) ++{ ++ WCHAR *dest; ++ if (!src) return NULL; ++ dest = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(src) + 1) * sizeof(WCHAR)); ++ if (dest) lstrcpyW(dest, src); ++ return dest; ++} ++ ++static inline LPWSTR strAtoW(const char *str) ++{ ++ LPWSTR ret = NULL; ++ ++ if (str) ++ { ++ DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); ++ if ((ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) ++ MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); ++ } ++ ++ return ret; ++} ++ ++struct inf_value; ++struct inf_section; ++struct inf_file; ++ ++HRESULT inf_load(const char *path, struct inf_file **inf_file) DECLSPEC_HIDDEN; ++void inf_free(struct inf_file *inf) DECLSPEC_HIDDEN; ++ ++BOOL inf_next_section(struct inf_file *inf, struct inf_section **sec) DECLSPEC_HIDDEN; ++struct inf_section *inf_get_section(struct inf_file *inf, const char *name) DECLSPEC_HIDDEN; ++char *inf_section_get_name(struct inf_section *section) DECLSPEC_HIDDEN; ++BOOL inf_section_next_value(struct inf_section *sec, struct inf_value **value) DECLSPEC_HIDDEN; ++ ++struct inf_value *inf_get_value(struct inf_section *sec, const char *key) DECLSPEC_HIDDEN; ++char *inf_value_get_key(struct inf_value *value) DECLSPEC_HIDDEN; ++char *inf_value_get_value(struct inf_value *value) DECLSPEC_HIDDEN; ++ ++char *trim(char *str, char **last_chr, BOOL strip_quotes) DECLSPEC_HIDDEN; ++ ++void component_set_actual_download_size(ICifComponent *iface, DWORD size) DECLSPEC_HIDDEN; ++void component_set_downloaded(ICifComponent *iface, BOOL value) DECLSPEC_HIDDEN; ++void component_set_installed(ICifComponent *iface, BOOL value) DECLSPEC_HIDDEN; ++ char *component_get_id(ICifComponent *iface) DECLSPEC_HIDDEN; +diff --git a/include/inseng.idl b/include/inseng.idl +index 8a3f4c4d270..82927418a99 100644 +--- a/include/inseng.idl ++++ b/include/inseng.idl +@@ -1,5 +1,6 @@ + /* + * Copyright 2015 Jacek Caban for CodeWeavers ++ * Copyright 2016 Michael MĂ¼ller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -26,15 +27,231 @@ cpp_quote("#endif") + + interface IStream; + +-/* FIXME: Add full declarations. */ +-interface ICifComponent; +-interface IEnumCifComponents; +-interface ICifGroup; +-interface IEnumCifGroups; +-interface ICifMode; +-interface IEnumCifModes; ++cpp_quote("#define MAX_ID_LENGTH 48") ++cpp_quote("#define MAX_DISPLAYNAME_LENGTH 128") + +-typedef struct { ++cpp_quote("#define URLF_DEFAULT 0x0") ++cpp_quote("#define URLF_EXTRACT 0x1") ++cpp_quote("#define URLF_RELATIVEURL 0x2") ++cpp_quote("#define URLF_DELETE_AFTER_EXTRACT 0x4") ++ ++cpp_quote("#define ENGINESTATUS_NOTREADY 0x0") ++cpp_quote("#define ENGINESTATUS_LOADING 0x1") ++cpp_quote("#define ENGINESTATUS_INSTALLING 0x2") ++cpp_quote("#define ENGINESTATUS_READY 0x3") ++ ++cpp_quote("#define SETACTION_NONE 0x0") ++cpp_quote("#define SETACTION_INSTALL 0x1") ++ ++cpp_quote("#define INSTALLOPTIONS_NOCACHE 0x01") ++cpp_quote("#define INSTALLOPTIONS_DOWNLOAD 0x02") ++cpp_quote("#define INSTALLOPTIONS_INSTALL 0x04") ++cpp_quote("#define INSTALLOPTIONS_DONTALLOWXPLATFORM 0x08") ++cpp_quote("#define INSTALLOPTIONS_FORCEDEPENDENCIES 0x10") ++ ++cpp_quote("#define EXECUTEJOB_SILENT 0x01") ++cpp_quote("#define EXECUTEJOB_DELETE_JOB 0x02") ++cpp_quote("#define EXECUTEJOB_VERIFYFILES 0x08") ++cpp_quote("#define EXECUTEJOB_IGNORETRUST 0x10") ++cpp_quote("#define EXECUTEJOB_IGNOREDOWNLOADERROR 0x20") ++cpp_quote("#define EXECUTEJOB_DONTALLOWCANCEL 0x40") ++ ++cpp_quote("#define ENGINEPROBLEM_DOWNLOADFAIL 0x1") ++ ++cpp_quote("#define PLATFORM_WIN95 0x01") ++cpp_quote("#define PLATFORM_WIN98 0x02") ++cpp_quote("#define PLATFORM_NT4 0x04") ++cpp_quote("#define PLATFORM_NT5 0x08") ++cpp_quote("#define PLATFORM_NT4ALPHA 0x10") ++cpp_quote("#define PLATFORM_NT5ALPHA 0x20") ++cpp_quote("#define PLATFORM_MILLEN 0x40") ++cpp_quote("#define PLATFORM_ALL (PLATFORM_WIN95 | PLATFORM_WIN98 | PLATFORM_NT4 | PLATFORM_NT5 | PLATFORM_NT4ALPHA | PLATFORM_NT5ALPHA | PLATFORM_MILLEN)") ++ ++enum InstallStatus ++{ ++ INSTALLSTATUS_INITIALIZING, ++ INSTALLSTATUS_DEPENDENCY, ++ INSTALLSTATUS_DOWNLOADING, ++ INSTALLSTATUS_COPYING, ++ INSTALLSTATUS_RETRYING, ++ INSTALLSTATUS_CHECKINGTRUST, ++ INSTALLSTATUS_EXTRACTING, ++ INSTALLSTATUS_RUNNING, ++ INSTALLSTATUS_FINISHED, ++ INSTALLSTATUS_DOWNLOADFINISHED, ++}; ++ ++enum ComponentAction ++{ ++ ActionNone, ++ ActionInstall, ++ ActionUninstall, ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifComponent ++{ ++ HRESULT GetID(char *id, DWORD size); ++ HRESULT GetGUID(char *guid, DWORD size); ++ HRESULT GetDescription(char *desc, DWORD size); ++ HRESULT GetDetails(char *details, DWORD size); ++ HRESULT GetUrl(UINT index, char *url, DWORD size, DWORD *flags); ++ HRESULT GetFileExtractList(UINT index, char *extract, DWORD size); ++ HRESULT GetUrlCheckRange(UINT index, DWORD *min, DWORD *max); ++ HRESULT GetCommand(UINT index, char *cmd, DWORD cmd_size, char *switches, DWORD switch_size, DWORD *type); ++ HRESULT GetVersion(DWORD *version, DWORD *build); ++ HRESULT GetLocale(char *pszLocale, DWORD size); ++ HRESULT GetUninstallKey(char *key, DWORD size); ++ HRESULT GetInstalledSize(DWORD *win, DWORD *app); ++ DWORD GetDownloadSize(); ++ DWORD GetExtractSize(); ++ HRESULT GetSuccessKey(char *key, DWORD size); ++ HRESULT GetProgressKeys(char *progress, DWORD progress_size, char *cancel, DWORD cancel_size); ++ HRESULT IsActiveSetupAware(); ++ HRESULT IsRebootRequired(); ++ HRESULT RequiresAdminRights(); ++ DWORD GetPriority(); ++ HRESULT GetDependency(UINT index, char *id, DWORD buf, char *type, DWORD *ver, DWORD *build); ++ DWORD GetPlatform(); ++ HRESULT GetMode(UINT index, char *mode, DWORD size); ++ HRESULT GetGroup(char *id, DWORD size); ++ HRESULT IsUIVisible(); ++ HRESULT GetPatchID(char *id, DWORD size); ++ HRESULT GetDetVersion(char *dll, DWORD dll_size, char *entry, DWORD entry_size); ++ HRESULT GetTreatAsOneComponents(UINT index, char *id, DWORD buf); ++ HRESULT GetCustomData(char *key, char *data, DWORD size); ++ DWORD IsComponentInstalled(); ++ HRESULT IsComponentDownloaded(); ++ DWORD IsThisVersionInstalled(DWORD version, DWORD build, DWORD *ret_version, DWORD *ret_build); ++ DWORD GetInstallQueueState(); ++ HRESULT SetInstallQueueState(DWORD state); ++ DWORD GetActualDownloadSize(); ++ DWORD GetCurrentPriority(); ++ HRESULT SetCurrentPriority(DWORD priority); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifRWComponent : ICifComponent ++{ ++ HRESULT SetGUID(const char *guid); ++ HRESULT SetDescription(const char *desc); ++ HRESULT SetUrl(UINT index, const char *url, DWORD url_flags); ++ HRESULT SetCommand(UINT index, const char *cmd, const char *switches, DWORD type); ++ HRESULT SetVersion(const char *version); ++ HRESULT SetUninstallKey(const char *key); ++ HRESULT SetInstalledSize(DWORD win, DWORD app); ++ HRESULT SetDownloadSize(DWORD size); ++ HRESULT SetExtractSize(DWORD size); ++ HRESULT DeleteDependency(const char *id, char type); ++ HRESULT AddDependency(const char *id, char type); ++ HRESULT SetUIVisible(BOOL visible); ++ HRESULT SetGroup(const char *id); ++ HRESULT SetPlatform(DWORD platform); ++ HRESULT SetPriority(DWORD priority); ++ HRESULT SetReboot(BOOL reboot); ++ HRESULT DeleteFromModes(const char *mode); ++ HRESULT AddToMode(const char *mode); ++ HRESULT SetModes(const char *mode); ++ HRESULT CopyComponent(const char *ciffile); ++ HRESULT AddToTreatAsOne(const char *compid); ++ HRESULT SetDetails(const char *desc); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface IEnumCifComponents : IUnknown ++{ ++ HRESULT Next(ICifComponent **); ++ HRESULT Reset(); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifGroup ++{ ++ HRESULT GetID(char *id, DWORD size); ++ HRESULT GetDescription(char *desc, DWORD size); ++ DWORD GetPriority(); ++ ++ HRESULT EnumComponents(IEnumCifComponents **, DWORD filter, void *pv); ++ DWORD GetCurrentPriority(); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifRWGroup : ICifGroup ++{ ++ HRESULT SetDescription(const char *desc); ++ HRESULT SetPriority(DWORD priority); ++ HRESULT SetDetails(const char *details); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface IEnumCifGroups : IUnknown ++{ ++ HRESULT Next(ICifGroup **); ++ HRESULT Reset(); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifMode ++{ ++ HRESULT GetID(char *id, DWORD size); ++ HRESULT GetDescription(char *desc, DWORD size); ++ HRESULT GetDetails(char *details, DWORD size); ++ ++ HRESULT EnumComponents(IEnumCifComponents **, DWORD filter, void *pv); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifRWMode : ICifMode ++{ ++ HRESULT SetDescription(const char *desc); ++ HRESULT SetDetails(const char *details); ++}; ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface IEnumCifModes : IUnknown ++{ ++ HRESULT Next(ICifMode **); ++ HRESULT Reset(); ++}; ++ ++typedef struct ++{ + DWORD cbSize; + DWORD dwInstallSize; + DWORD dwWinDriveSize; +@@ -49,6 +266,15 @@ typedef struct { + DWORD dwTotalDownloadSize; + } COMPONENT_SIZES; + ++typedef struct ++{ ++ DWORD cbSize; ++ DWORD dwDownloadKBRemaining; ++ DWORD dwInstallKBRemaining; ++ DWORD dwDownloadSecsRemaining; ++ DWORD dwInstallSecsRemaining; ++} INSTALLPROGRESS; ++ + [ + uuid(6e449688-c509-11cf-aafa-00aa00b6015c), + local +@@ -62,7 +288,24 @@ interface ICifFile : IUnknown + HRESULT EnumModes(IEnumCifModes **cuf_modes, DWORD filter, void *pv); + HRESULT FindMode(const char *id, ICifMode **p); + HRESULT GetDescription(char *desc, DWORD size); +- HRESULT GetDetDlls(char **dlls, DWORD size); ++ HRESULT GetDetDlls(char *dlls, DWORD size); ++} ++ ++[ ++ object, ++ local, ++ pointer_default(unique) ++] ++interface ICifRWFile : ICifFile ++{ ++ HRESULT SetDescription(const char *desc); ++ HRESULT CreateComponent(const char *id, ICifRWComponent **p); ++ HRESULT CreateGroup(const char *id, ICifRWGroup **p); ++ HRESULT CreateMode(const char *id, ICifRWMode **p); ++ HRESULT DeleteComponent(const char *id); ++ HRESULT DeleteGroup(const char *id); ++ HRESULT DeleteMode(const char *id); ++ HRESULT Flush(); + } + + [ +@@ -78,7 +321,7 @@ interface IInstallEngineCallback : IUnknown + const char *msg_string, ULONG progress, ULONG max); + HRESULT OnStopComponent(const char *id, HRESULT error, DWORD phrase, const char *string, DWORD status); + HRESULT OnStopInstall(HRESULT error, const char *error_string, DWORD status); +- HRESULT OnEngineProblem(DWORD problem, LPDWORD action); ++ HRESULT OnEngineProblem(DWORD problem, DWORD *action); + } + + [ +@@ -121,6 +364,16 @@ interface IInstallEngine2 : IInstallEngine + HRESULT GetICifFile(ICifFile **cif_file); + } + ++[ ++ uuid(6e449687-c509-11cf-aafa-00aa00b6015c), ++ local ++] ++interface IInstallEngineTiming : IUnknown ++{ ++ HRESULT GetRates(DWORD *download, DWORD *install); ++ HRESULT GetInstallProgress(INSTALLPROGRESS *progress); ++} ++ + [ + helpstring("Microsoft Active Setup Engine"), + threading(apartment), +@@ -134,3 +387,6 @@ coclass InstallEngine { } + uuid(bfc880f1-7484-11d0-8309-00aa00b6015c) + ] + coclass DownloadSiteMgr { } ++ ++cpp_quote("HRESULT WINAPI GetICifFileFromFile(ICifFile **, const char *);") ++cpp_quote("HRESULT WINAPI GetICifRWFileFromFile(ICifRWFile **, const char *);") +-- +2.34.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/definition new file mode 100644 index 000000000..8758f4ee4 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/inseng-Implementation/definition @@ -0,0 +1 @@ +Fixes: [39456] Implement CIF reader and download functionality in inseng.dll diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch new file mode 100644 index 000000000..09878b224 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0001-loader-Add-Keyboard-Layouts-registry-enteries.patch @@ -0,0 +1,261 @@ +From f2347ae7216626d248cfaf9445862561b2b5eef7 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Mon, 1 Jul 2019 09:58:55 +1000 +Subject: [PATCH] loader: Add Keyboard Layouts registry enteries. + +Signed-off-by: Alistair Leslie-Hughes +--- + loader/wine.inf.in | 209 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 209 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index ff601e41b26..6ffb8c56d87 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -63,6 +63,7 @@ AddReg=\ + Debugger,\ + DirectX,\ + Fonts,\ ++ KeyboardLayouts,\ + MCI,\ + Misc,\ + OLE,\ +@@ -87,6 +88,7 @@ AddReg=\ + Debugger,\ + DirectX,\ + Fonts,\ ++ KeyboardLayouts,\ + MCI,\ + Misc,\ + OLE,\ +@@ -113,6 +115,7 @@ AddReg=\ + Debugger,\ + DirectX,\ + Fonts,\ ++ KeyboardLayouts,\ + MCI,\ + Misc,\ + OLE,\ +@@ -160,6 +163,7 @@ AddReg=\ + CurrentVersionWow64,\ + Debugger,\ + DirectX,\ ++ KeyboardLayouts,\ + MCI,\ + Misc,\ + Tapi,\ +@@ -597,6 +601,211 @@ HKCU,Software\Microsoft\Windows\Shell\Associations\UrlAssociations\ftp\UserChoic + HKCU,Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice,"ProgId",,"http" + HKCU,Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice,"ProgId",,"https" + ++[KeyboardLayouts] ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000401,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000402,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000404,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000405,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000406,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000407,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000040f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000410,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000411,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000412,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000413,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000414,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000415,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000416,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000418,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000419,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000041f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000420,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000422,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000423,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000424,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000425,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000426,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000427,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000428,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000429,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000042a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000042b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000042c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000042e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000042f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000432,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000437,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000438,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000439,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000043a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000043b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000043f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000440,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000442,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000444,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000445,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000446,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000447,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000448,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000449,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000044a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000044b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000044c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000044d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000044e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000450,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000451,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000452,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000453,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000454,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000045a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000045b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000045c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000461,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000463,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000465,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000468,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000046a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000046c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000046d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000046e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000046f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000470,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000474,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000475,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000480,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000481,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000485,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000488,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000492,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000804,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000807,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000809,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000080a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000080c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000813,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000816,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000081a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000082c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000083b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000843,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000850,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000085d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000085f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000c04,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000c0c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000c1a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00001004,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00001009,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000100c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000105f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00001404,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00001809,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0000201a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00004009,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010401,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010402,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010405,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010407,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001040a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001040e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010410,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010415,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010416,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010418,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010419,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001041b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001041e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001041f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010426,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010427,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001042b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001042c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001042e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001042f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010437,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010439,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001043a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001043b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010444,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010445,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010451,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010453,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001045a,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001045b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001045c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001045d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010465,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010480,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001080c,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001083b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010850,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00011009,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0001105f,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00011809,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020401,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020402,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020405,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0002040d,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020418,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020419,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0002041e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020422,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020426,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020427,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0002042b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0002042e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020437,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020445,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0002083b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00020c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030402,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0003041e,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\0003042b,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030437,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00040402,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00040408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00040409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00040437,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00040c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00050408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00050409,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00050429,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00060408,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00070c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00080c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00090c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000a0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000b0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000c0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000d0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000e0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\000f0c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00100c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00110c00,,16 ++HKLM,SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00120c00,,16 ++ + [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch new file mode 100644 index 000000000..afece280f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/0002-user32-Improve-GetKeyboardLayoutList.patch @@ -0,0 +1,133 @@ +From f57e7a06cffc47773c647a10ed4d298b58fbd408 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 9 Jul 2019 14:13:28 +1000 +Subject: [PATCH] user32: Do not enumerate the registry in + GetKeyboardLayoutList(). + +This function returns the current list of *installed* Keyboard layouts +not the complete list from the registry. +--- + dlls/user32/input.c | 1 - + dlls/user32/tests/input.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/win32u/input.c | 33 +-------------------------------- + 3 files changed, 36 insertions(+), 33 deletions(-) + +diff --git a/dlls/user32/input.c b/dlls/user32/input.c +index c5387cf9212..2507a86e3b6 100644 +--- a/dlls/user32/input.c ++++ b/dlls/user32/input.c +@@ -494,7 +494,6 @@ BOOL WINAPI UnloadKeyboardLayout( HKL layout ) + return FALSE; + } + +- + /*********************************************************************** + * EnableMouseInPointer (USER32.@) + */ +diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c +index bf913b8e6a6..8ac46bedc71 100644 +--- a/dlls/user32/tests/input.c ++++ b/dlls/user32/tests/input.c +@@ -4837,6 +4837,40 @@ static void test_EnableMouseInPointer( char **argv, BOOL enable ) + CloseHandle( info.hProcess ); + } + ++static void test_GetKeyboardLayoutList(void) ++{ ++ int cnt, cnt2; ++ HKL *layouts; ++ ULONG_PTR baselayout; ++ LANGID langid; ++ ++ baselayout = GetUserDefaultLCID(); ++ langid = PRIMARYLANGID(LANGIDFROMLCID(baselayout)); ++ if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN) ++ baselayout = MAKELONG( baselayout, 0xe001 ); /* IME */ ++ else ++ baselayout |= baselayout << 16; ++ ++ cnt = GetKeyboardLayoutList(0, NULL); ++ /* Most users will not have more than a few keyboard layouts installed at a time. */ ++ ok(cnt > 0 && cnt < 10, "Layout count %d\n", cnt); ++ if (cnt > 0) ++ { ++ layouts = HeapAlloc(GetProcessHeap(), 0, sizeof(*layouts) * cnt ); ++ ++ cnt2 = GetKeyboardLayoutList(cnt, layouts); ++ ok(cnt == cnt2, "wrong value %d!=%d\n", cnt, cnt2); ++ for(cnt = 0; cnt < cnt2; cnt++) ++ { ++ if(layouts[cnt] == (HKL)baselayout) ++ break; ++ } ++ ok(cnt < cnt2, "Didnt find current keyboard\n"); ++ ++ HeapFree(GetProcessHeap(), 0, layouts); ++ } ++} ++ + START_TEST(input) + { + char **argv; +@@ -4887,6 +4921,7 @@ START_TEST(input) + test_GetRawInputBuffer(); + test_RegisterRawInputDevices(); + test_rawinput(argv[0]); ++ test_GetKeyboardLayoutList(); + test_DefRawInputProc(); + + if(pGetMouseMovePointsEx) +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index fd16d03f733..fb452843803 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -934,11 +934,7 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) + */ + UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts ) + { +- char buffer[4096]; +- KEY_NODE_INFORMATION *key_info = (KEY_NODE_INFORMATION *)buffer; +- KEY_VALUE_PARTIAL_INFORMATION *value_info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; +- DWORD count, tmp, i = 0; +- HKEY hkey, subkey; ++ DWORD count; + HKL layout; + + TRACE_(keyboard)( "size %d, layouts %p.\n", size, layouts ); +@@ -1277,33 +1273,6 @@ UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts ) + if (size && layouts) + { + layouts[count - 1] = layout; +- if (count == size) return count; +- } +- +- if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) ))) +- { +- while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key_info, +- sizeof(buffer) - sizeof(WCHAR), &tmp )) +- { +- if (!(subkey = reg_open_key( hkey, key_info->Name, key_info->NameLength ))) continue; +- key_info->Name[key_info->NameLength / sizeof(WCHAR)] = 0; +- tmp = wcstoul( key_info->Name, NULL, 16 ); +- if (query_reg_ascii_value( subkey, "Layout Id", value_info, sizeof(buffer) ) && +- value_info->Type == REG_SZ) +- tmp = 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff); +- NtClose( subkey ); +- +- tmp = MAKELONG( LOWORD( layout ), LOWORD( tmp ) ); +- if (layout == UlongToHandle( tmp )) continue; +- +- count++; +- if (size && layouts) +- { +- layouts[count - 1] = UlongToHandle( tmp ); +- if (count == size) break; +- } +- } +- NtClose( hkey ); + } + + return count; +-- +2.39.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/definition new file mode 100644 index 000000000..310136ea7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/loader-KeyboardLayouts/definition @@ -0,0 +1 @@ +Fixes: [47439] loader: Add Keyboard Layouts registry enteries. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch new file mode 100644 index 000000000..62887e1f6 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/0003-ntdll-tests-Add-basic-tests-for-RtlQueryPackageIdent.patch @@ -0,0 +1,143 @@ +From 7e1c63548513ec95978c5cbc428555ac5ed7c1d2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Sun, 17 Jan 2016 00:50:50 +0100 +Subject: [PATCH] ntdll/tests: Add basic tests for RtlQueryPackageIdentity. + +--- + dlls/ntdll/tests/Makefile.in | 2 +- + dlls/ntdll/tests/rtl.c | 80 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 81 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in +index 90deb5865f8..428ebde23b3 100644 +--- a/dlls/ntdll/tests/Makefile.in ++++ b/dlls/ntdll/tests/Makefile.in +@@ -1,5 +1,5 @@ + TESTDLL = ntdll.dll +-IMPORTS = user32 advapi32 ++IMPORTS = user32 ole32 advapi32 + + C_SRCS = \ + atom.c \ +diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c +index 1e5e8c2580c..3690402c268 100644 +--- a/dlls/ntdll/tests/rtl.c ++++ b/dlls/ntdll/tests/rtl.c +@@ -28,6 +28,9 @@ + #include "inaddr.h" + #include "ip2string.h" + #include "wine/asm.h" ++#include "initguid.h" ++#define COBJMACROS ++#include "shobjidl.h" + + #ifndef __WINE_WINTERNL_H + +@@ -95,6 +98,9 @@ static BOOL (WINAPI *pRtlIsCriticalSectionLocked)(CRITICAL_SECTION *); + static BOOL (WINAPI *pRtlIsCriticalSectionLockedByThread)(CRITICAL_SECTION *); + static NTSTATUS (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, ULONG, ULONG); + static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *); ++static NTSTATUS (WINAPI *pRtlQueryPackageIdentity)(HANDLE, WCHAR*, SIZE_T*, WCHAR*, SIZE_T*, BOOLEAN*); ++static NTSTATUS (WINAPI *pRtlMakeSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,LPDWORD); ++static NTSTATUS (WINAPI *pRtlAbsoluteToSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PULONG); + static NTSTATUS (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **); + static NTSTATUS (WINAPI *pLdrUnregisterDllNotification)(void *); + +@@ -137,6 +143,9 @@ static void InitFunctionPtrs(void) + pRtlIsCriticalSectionLockedByThread = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLockedByThread"); + pRtlInitializeCriticalSectionEx = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSectionEx"); + pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules"); ++ pRtlQueryPackageIdentity = (void *)GetProcAddress(hntdll, "RtlQueryPackageIdentity"); ++ pRtlMakeSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlMakeSelfRelativeSD"); ++ pRtlAbsoluteToSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlAbsoluteToSelfRelativeSD"); + pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification"); + pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification"); + } +@@ -3775,6 +3784,76 @@ static void test_RtlFirstFreeAce(void) + HeapFree(GetProcessHeap(), 0, acl); + } + ++static void test_RtlQueryPackageIdentity(void) ++{ ++ const WCHAR programW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.', ++ 'P','h','o','t','o','s','_','8','w','e','k','y','b','3','d','8','b','b','w','e','!','A','p','p',0}; ++ const WCHAR fullnameW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.', ++ 'P','h','o','t','o','s', 0}; ++ const WCHAR appidW[] = {'A','p','p',0}; ++ IApplicationActivationManager *manager; ++ WCHAR buf1[MAX_PATH], buf2[MAX_PATH]; ++ HANDLE process, token; ++ SIZE_T size1, size2; ++ NTSTATUS status; ++ DWORD processid; ++ HRESULT hr; ++ BOOL ret; ++ ++ if (!pRtlQueryPackageIdentity) ++ { ++ win_skip("RtlQueryPackageIdentity not available\n"); ++ return; ++ } ++ ++ size1 = size2 = MAX_PATH * sizeof(WCHAR); ++ status = pRtlQueryPackageIdentity((HANDLE)~(ULONG_PTR)3, buf1, &size1, buf2, &size2, NULL); ++ ok(status == STATUS_NOT_FOUND, "expected STATUS_NOT_FOUND, got %08x\n", status); ++ ++ CoInitializeEx(0, COINIT_APARTMENTTHREADED); ++ hr = CoCreateInstance(&CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, ++ &IID_IApplicationActivationManager, (void **)&manager); ++ if (FAILED(hr)) ++ { ++ todo_wine win_skip("Failed to create ApplicationActivationManager (%x)\n", hr); ++ goto done; ++ } ++ ++ hr = IApplicationActivationManager_ActivateApplication(manager, programW, NULL, ++ AO_NOERRORUI, &processid); ++ if (FAILED(hr)) ++ { ++ todo_wine win_skip("Failed to start program (%x)\n", hr); ++ IApplicationActivationManager_Release(manager); ++ goto done; ++ } ++ ++ process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, FALSE, processid); ++ ok(process != NULL, "OpenProcess failed with %u\n", GetLastError()); ++ ret = OpenProcessToken(process, TOKEN_QUERY, &token); ++ ok(ret, "OpenProcessToken failed with error %u\n", GetLastError()); ++ ++ size1 = size2 = MAX_PATH * sizeof(WCHAR); ++ status = pRtlQueryPackageIdentity(token, buf1, &size1, buf2, &size2, NULL); ++ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); ++ ++ ok(!memcmp(buf1, fullnameW, sizeof(fullnameW) - sizeof(WCHAR)), ++ "Expected buf1 to begin with %s, got %s\n", wine_dbgstr_w(fullnameW), wine_dbgstr_w(buf1)); ++ ok(size1 >= sizeof(WCHAR) && !(size1 % sizeof(WCHAR)), "Unexpected size1 = %lu\n", size1); ++ ok(buf1[size1 / sizeof(WCHAR) - 1] == 0, "Expected buf1[%lu] == 0\n", size1 / sizeof(WCHAR) - 1); ++ ++ ok(!lstrcmpW(buf2, appidW), "Expected buf2 to be %s, got %s\n", wine_dbgstr_w(appidW), wine_dbgstr_w(buf2)); ++ ok(size2 >= sizeof(WCHAR) && !(size2 % sizeof(WCHAR)), "Unexpected size2 = %lu\n", size2); ++ ok(buf2[size2 / sizeof(WCHAR) - 1] == 0, "Expected buf2[%lu] == 0\n", size2 / sizeof(WCHAR) - 1); ++ ++ CloseHandle(token); ++ TerminateProcess(process, 0); ++ CloseHandle(process); ++ ++done: ++ CoUninitialize(); ++} ++ + START_TEST(rtl) + { + InitFunctionPtrs(); +@@ -3814,6 +3893,7 @@ START_TEST(rtl) + test_RtlInitializeCriticalSectionEx(); + test_RtlLeaveCriticalSection(); + test_LdrEnumerateLoadedModules(); ++ test_RtlQueryPackageIdentity(); + test_RtlMakeSelfRelativeSD(); + test_LdrRegisterDllNotification(); + test_DbgPrint(); +-- +2.39.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/definition new file mode 100644 index 000000000..6672b6650 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-RtlQueryPackageIdentity/definition @@ -0,0 +1 @@ +Fixes: Add stub for ntdll.RtlQueryPackageIdentity diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch new file mode 100644 index 000000000..2b8bb9378 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/0001-ntdll-Do-a-device-check-before-returning-a-default-s.patch @@ -0,0 +1,81 @@ +From 0c71b9c48afdc0478941417595998ab21fcf12ae Mon Sep 17 00:00:00 2001 +From: Alex Henrie +Date: Tue, 29 Dec 2015 00:48:02 -0700 +Subject: [PATCH] mountmgr.sys: Do a device check before returning a default + serial port name. + +Fixes https://bugs.winehq.org/show_bug.cgi?id=39793 +--- + dlls/mountmgr.sys/device.c | 2 +- + dlls/mountmgr.sys/unixlib.c | 22 ++++++++++++++++++++++ + dlls/mountmgr.sys/unixlib.h | 1 + + 3 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c +index 328b0b2f344..0757036c8e3 100644 +--- a/dlls/mountmgr.sys/device.c ++++ b/dlls/mountmgr.sys/device.c +@@ -1884,7 +1884,7 @@ static BOOL create_port_device( DRIVER_OBJECT *driver, int n, const char *unix_p + UNICODE_STRING nt_name, symlink_name, default_name; + DEVICE_OBJECT *dev_obj; + NTSTATUS status; +- struct set_dosdev_symlink_params params = { dosdevices_path, unix_path }; ++ struct set_dosdev_symlink_params params = { dosdevices_path, unix_path, driver == serial_driver }; + + /* create DOS device */ + if (MOUNTMGR_CALL( set_dosdev_symlink, ¶ms )) return FALSE; +diff --git a/dlls/mountmgr.sys/unixlib.c b/dlls/mountmgr.sys/unixlib.c +index 13f6fbecf09..0332f6f6018 100644 +--- a/dlls/mountmgr.sys/unixlib.c ++++ b/dlls/mountmgr.sys/unixlib.c +@@ -36,6 +36,7 @@ + #ifdef HAVE_SYS_STATVFS_H + # include + #endif ++#include + #include + + #include "unixlib.h" +@@ -304,6 +305,27 @@ static NTSTATUS set_dosdev_symlink( void *args ) + char *path; + NTSTATUS status = STATUS_SUCCESS; + ++#ifdef linux ++ /* Serial port device files almost always exist on Linux even if the corresponding serial ++ * ports don't exist. Do a basic functionality check before advertising a serial port. */ ++ if (params->serial) ++ { ++ struct termios tios; ++ int fd; ++ ++ if ((fd = open( params->dest, O_RDONLY )) == -1) ++ return FALSE; ++ ++ if (tcgetattr( fd, &tios ) == -1) ++ { ++ close( fd ); ++ return FALSE; ++ } ++ ++ close( fd ); ++ } ++#endif ++ + if (!(path = get_dosdevices_path( params->dev ))) return STATUS_NO_MEMORY; + + if (params->dest && params->dest[0]) +diff --git a/dlls/mountmgr.sys/unixlib.h b/dlls/mountmgr.sys/unixlib.h +index d70371876fa..ef5b10732f6 100644 +--- a/dlls/mountmgr.sys/unixlib.h ++++ b/dlls/mountmgr.sys/unixlib.h +@@ -90,6 +90,7 @@ struct set_dosdev_symlink_params + { + const char *dev; + const char *dest; ++ BOOL serial; + }; + + struct get_volume_dos_devices_params +-- +2.37.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/definition new file mode 100644 index 000000000..95fe42a7b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/ntdll-Serial_Port_Detection/definition @@ -0,0 +1 @@ +Fixes: [39793] Do a device check before returning a default serial port name diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/0001-packager-Prefer-native-version.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/0001-packager-Prefer-native-version.patch new file mode 100644 index 000000000..6ba9b0658 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/0001-packager-Prefer-native-version.patch @@ -0,0 +1,25 @@ +From cea29373b287ae84e28adca9834ffdbb97844ffe Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Sun, 6 Aug 2017 02:50:23 +0200 +Subject: [PATCH] packager: Prefer native version. + +--- + dlls/packager/Makefile.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/packager/Makefile.in b/dlls/packager/Makefile.in +index f539cb6f095..faef8deb263 100644 +--- a/dlls/packager/Makefile.in ++++ b/dlls/packager/Makefile.in +@@ -2,6 +2,8 @@ EXTRADEFS = -DWINE_NO_LONG_TYPES + MODULE = packager.dll + IMPORTS = uuid shell32 shlwapi user32 + ++EXTRADLLFLAGS = -Wb,--prefer-native ++ + C_SRCS = \ + packager_main.c + +-- +2.34.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/definition new file mode 100644 index 000000000..83e85b8bb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/packager-DllMain/definition @@ -0,0 +1 @@ +Fixes: [43472] Prefer native version of packager.dll diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch new file mode 100644 index 000000000..6823d77b6 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/rawinput/0006-winex11.drv-Send-relative-RawMotion-events-unprocess.patch @@ -0,0 +1,106 @@ +From 7e0df0539b242a4a87b8189232eefb3164fe4617 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 25 Oct 2021 11:48:00 +0200 +Subject: [PATCH] winex11.drv: Send relative RawMotion events unprocessed. + +This makes relative raw input independent from cursor speed, as it is +the case on Windows. Absolute raw input is already translated to +virtual desktop space, and cursor speed is meaningless in this case. + +This does not support mixed relative/absolute X/Y axis. +--- + dlls/winex11.drv/mouse.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c +index 739960b083e..b5f91634e94 100644 +--- a/dlls/winex11.drv/mouse.c ++++ b/dlls/winex11.drv/mouse.c +@@ -1925,12 +1925,12 @@ static BOOL X11DRV_XIDeviceChangedEvent( XIDeviceChangedEvent *event ) + return TRUE; + } + +-static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) ++static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input, RAWINPUT *rawinput ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XIValuatorClassInfo *x = &thread_data->x_valuator, *y = &thread_data->y_valuator; +- double x_value = 0, y_value = 0, x_scale, y_scale, user_to_real_scale; +- const double *values = event->valuators.values; ++ const double *values = event->valuators.values, *raw_values = event->raw_values; ++ double x_raw = 0, y_raw = 0, x_value = 0, y_value = 0, x_scale, y_scale, user_to_real_scale; + RECT virtual_rect; + HMONITOR monitor; + POINT pt; +@@ -1961,33 +1961,36 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) + if (!XIMaskIsSet( event->valuators.mask, i )) continue; + if (i == x->number) + { ++ x_raw = *raw_values; + x_value = *values; + if (x->mode == XIModeRelative) x->value += x_value * x_scale; + else x->value = (x_value - x->min) * x_scale; + } + if (i == y->number) + { ++ y_raw = *raw_values; + y_value = *values; + if (y->mode == XIModeRelative) y->value += y_value * y_scale; + else y->value = (y_value - y->min) * y_scale; + } ++ raw_values++; + values++; + } + + input->u.mi.dx = round( x->value ); + input->u.mi.dy = round( y->value ); + ++ if (x->mode != XIModeAbsolute) rawinput->data.mouse.lLastX = x_raw; ++ else rawinput->data.mouse.lLastX = input->u.mi.dx; ++ if (y->mode != XIModeAbsolute) rawinput->data.mouse.lLastY = y_raw; ++ else rawinput->data.mouse.lLastY = input->u.mi.dy; ++ + TRACE( "event %f,%f value %f,%f input %d,%d\n", x_value, y_value, x->value, y->value, + (int)input->u.mi.dx, (int)input->u.mi.dy ); + + x->value -= input->u.mi.dx; + y->value -= input->u.mi.dy; + +- if (!(input->u.mi.dwFlags & MOUSEEVENTF_ABSOLUTE) && !input->u.mi.dx && !input->u.mi.dy) +- { +- TRACE( "accumulating motion\n" ); +- return FALSE; +- } + + if (input->u.mi.dwFlags & MOUSEEVENTF_ABSOLUTE) + fs_hack_point_real_to_user( (POINT *)&input->u.mi.dx ); +@@ -2029,7 +2032,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) + input.u.mi.dwExtraInfo = 0; + input.u.mi.dx = 0; + input.u.mi.dy = 0; +- if (!map_raw_event_coords( event, &input )) return FALSE; ++ if (!map_raw_event_coords( event, &input, &rawinput )) return FALSE; + + if (!thread_data->xi2_rawinput_only) + __wine_send_input( 0, &input, NULL ); +@@ -2043,8 +2046,6 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) + rawinput.data.mouse.ulRawButtons = 0; + rawinput.data.mouse.u.usButtonData = 0; + rawinput.data.mouse.u.usButtonFlags = 0; +- rawinput.data.mouse.lLastX = input.u.mi.dx; +- rawinput.data.mouse.lLastY = input.u.mi.dy; + rawinput.data.mouse.ulExtraInformation = 0; + + input.type = INPUT_HARDWARE; +@@ -2231,7 +2232,7 @@ static BOOL X11DRV_RawTouchEvent( XGenericEventCookie *xev ) + POINT pos; + + if (!thread_data->xi2_rawinput_only) return FALSE; +- if (!map_raw_event_coords( event, &input )) return FALSE; ++ if (!map_raw_event_coords( event, &input, &rawinput )) return FALSE; + if (!(input.u.mi.dwFlags & MOUSEEVENTF_ABSOLUTE)) return FALSE; + pos.x = input.u.mi.dx; + pos.y = input.u.mi.dy; +-- +2.38.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/0001-shell32-Implement-NewMenu-with-new-folder-item.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/0001-shell32-Implement-NewMenu-with-new-folder-item.patch new file mode 100644 index 000000000..0d6ec9254 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/0001-shell32-Implement-NewMenu-with-new-folder-item.patch @@ -0,0 +1,607 @@ +From 69b8c9461157d1b988ec039c4f7e7a467cb9e951 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Sun, 16 Aug 2015 17:34:22 +0200 +Subject: [PATCH] shell32: Implement NewMenu with new folder item. + +v2: +Added and Correct tests +Use IContextMenu3 for all IContextMenu* interfaces. +Use heap_alloc/free functions + +v3: +Correct header issue when compiling i386 (var_arg) +--- + dlls/shell32/Makefile.in | 1 + + dlls/shell32/shell32_classes.idl | 5 + + dlls/shell32/shell32_main.h | 1 + + dlls/shell32/shellnew.c | 497 +++++++++++++++++++++++++++++++ + dlls/shell32/shellole.c | 1 + + dlls/shell32/tests/shlview.c | 9 +- + 6 files changed, 513 insertions(+), 1 deletion(-) + create mode 100644 dlls/shell32/shellnew.c + +diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in +index 9e2395126fc..3bba1b0e3fd 100644 +--- a/dlls/shell32/Makefile.in ++++ b/dlls/shell32/Makefile.in +@@ -28,6 +28,7 @@ C_SRCS = \ + shelldispatch.c \ + shellitem.c \ + shelllink.c \ ++ shellnew.c \ + shellole.c \ + shellord.c \ + shellpath.c \ +diff --git a/dlls/shell32/shell32_classes.idl b/dlls/shell32/shell32_classes.idl +index dc65ed3728d..c5f4215196f 100644 +--- a/dlls/shell32/shell32_classes.idl ++++ b/dlls/shell32/shell32_classes.idl +@@ -86,6 +86,11 @@ coclass KnownFolderManager { interface IKnownFolderManager; } + uuid(4657278a-411b-11d2-839a-00c04fd918d0) + ] coclass DragDropHelper { interface IDropTargetHelper; } + ++[ ++ threading(apartment), ++ uuid(d969a300-e7ff-11d0-a93b-00a0c90f2719) ++] coclass NewMenu { interface IShellExtInit; } ++ + [ + threading(apartment), + uuid(00bb2763-6a77-11d0-a535-00c04fd7d062) +diff --git a/dlls/shell32/shell32_main.h b/dlls/shell32/shell32_main.h +index 7bb26e46a6e..f539a1b1e00 100644 +--- a/dlls/shell32/shell32_main.h ++++ b/dlls/shell32/shell32_main.h +@@ -101,6 +101,7 @@ HRESULT WINAPI RecycleBin_Constructor(IUnknown * pUnkOuter, REFIID riif, LPVOID + HRESULT WINAPI QueryAssociations_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput) DECLSPEC_HIDDEN; + HRESULT WINAPI ExplorerBrowser_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; + HRESULT WINAPI KnownFolderManager_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; ++HRESULT WINAPI NewMenu_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; + HRESULT WINAPI IFileOperation_Constructor(IUnknown *outer, REFIID riid, void **out) DECLSPEC_HIDDEN; + HRESULT WINAPI ActiveDesktop_Constructor(IUnknown *outer, REFIID riid, void **out) DECLSPEC_HIDDEN; + +diff --git a/dlls/shell32/shellnew.c b/dlls/shell32/shellnew.c +new file mode 100644 +index 00000000000..ba31b3787f8 +--- /dev/null ++++ b/dlls/shell32/shellnew.c +@@ -0,0 +1,497 @@ ++/* ++ * Copyright 2015 Michael MĂ¼ller ++ * ++ * 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 ++ */ ++ ++#define COBJMACROS ++#define NONAMELESSUNION ++ ++#include ++ ++#include "winerror.h" ++#include "windef.h" ++#include "winbase.h" ++#include "winnls.h" ++#include "winreg.h" ++ ++#include "winuser.h" ++#include "wingdi.h" ++#include "shlobj.h" ++ ++ ++#include "pidl.h" ++#include "shell32_main.h" ++#include "shlguid.h" ++#include "shlwapi.h" ++#include "shresdef.h" ++#include "shellfolder.h" ++ ++#include "wine/heap.h" ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(shell); ++ ++typedef struct ++{ ++ IShellExtInit IShellExtInit_iface; ++ IContextMenu3 IContextMenu3_iface; ++ IObjectWithSite IObjectWithSite_iface; ++ ++ LONG ref; ++ IUnknown *site; ++ LPITEMIDLIST pidl; ++ HICON icon_folder; ++ ++ UINT folder_cmd; ++} NewMenuImpl; ++ ++static inline NewMenuImpl *impl_from_IShellExtInit(IShellExtInit *iface) ++{ ++ return CONTAINING_RECORD(iface, NewMenuImpl, IShellExtInit_iface); ++} ++ ++static inline NewMenuImpl *impl_from_IContextMenu3(IContextMenu3 *iface) ++{ ++ return CONTAINING_RECORD(iface, NewMenuImpl, IContextMenu3_iface); ++} ++ ++static inline NewMenuImpl *impl_from_IObjectWithSite(IObjectWithSite *iface) ++{ ++ return CONTAINING_RECORD(iface, NewMenuImpl, IObjectWithSite_iface); ++} ++ ++static HRESULT WINAPI ++NewMenu_ExtInit_QueryInterface(IShellExtInit *iface, REFIID riid, void **ppv) ++{ ++ NewMenuImpl *This = impl_from_IShellExtInit(iface); ++ TRACE("(%p)->(%s)\n", This, debugstr_guid(riid)); ++ ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || ++ IsEqualIID(riid, &IID_IShellExtInit)) ++ { ++ *ppv = &This->IShellExtInit_iface; ++ } ++ else if (IsEqualIID(riid, &IID_IObjectWithSite)) ++ { ++ *ppv = &This->IObjectWithSite_iface; ++ } ++ else if (IsEqualIID(riid, &IID_IContextMenu) || ++ IsEqualIID(riid, &IID_IContextMenu2) || ++ IsEqualIID(riid, &IID_IContextMenu3)) ++ { ++ *ppv = &This->IContextMenu3_iface; ++ } ++ ++ if (*ppv) ++ { ++ IUnknown_AddRef((IUnknown *)*ppv); ++ TRACE("-- Interface: (%p)->(%p)\n", ppv, *ppv); ++ return S_OK; ++ } ++ ++ ERR("-- Interface: E_NOINTERFACE for %s\n", debugstr_guid(riid)); ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI ++NewMenu_ExtInit_AddRef(IShellExtInit *iface) ++{ ++ NewMenuImpl *This = impl_from_IShellExtInit(iface); ++ ULONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("(%p), refcount=%lu\n", iface, ref); ++ ++ return ref; ++} ++ ++static ULONG WINAPI ++NewMenu_ExtInit_Release(IShellExtInit *iface) ++{ ++ NewMenuImpl *This = impl_from_IShellExtInit(iface); ++ ULONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("(%p), refcount=%lu\n", iface, ref); ++ ++ if (!ref) ++ { ++ if (This->site) IUnknown_Release(This->site); ++ if (This->pidl) ILFree(This->pidl); ++ heap_free(This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI ++NewMenu_ExtInit_Initialize(IShellExtInit *iface, LPCITEMIDLIST pidl, IDataObject *obj, HKEY key) ++{ ++ NewMenuImpl *This = impl_from_IShellExtInit(iface); ++ ++ TRACE("(%p)->(%p, %p, %p)\n", This, pidl, obj, key ); ++ ++ if (!pidl) ++ return E_FAIL; ++ ++ if (This->pidl) ILFree(This->pidl); ++ This->pidl = ILClone(pidl); ++ This->icon_folder = LoadImageW(shell32_hInstance, (LPCWSTR)MAKEINTRESOURCE(IDI_SHELL_FOLDER), IMAGE_ICON, ++ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED); ++ ++ return S_OK; ++} ++ ++static const IShellExtInitVtbl eivt = ++{ ++ NewMenu_ExtInit_QueryInterface, ++ NewMenu_ExtInit_AddRef, ++ NewMenu_ExtInit_Release, ++ NewMenu_ExtInit_Initialize ++}; ++ ++ ++static HRESULT WINAPI ++NewMenu_ObjectWithSite_QueryInterface(IObjectWithSite *iface, REFIID riid, void **ppv) ++{ ++ NewMenuImpl *This = impl_from_IObjectWithSite(iface); ++ return NewMenu_ExtInit_QueryInterface(&This->IShellExtInit_iface, riid, ppv); ++} ++ ++static ULONG WINAPI ++NewMenu_ObjectWithSite_AddRef(IObjectWithSite *iface) ++{ ++ NewMenuImpl *This = impl_from_IObjectWithSite(iface); ++ return NewMenu_ExtInit_AddRef(&This->IShellExtInit_iface); ++} ++ ++static ULONG WINAPI ++NewMenu_ObjectWithSite_Release(IObjectWithSite *iface) ++{ ++ NewMenuImpl *This = impl_from_IObjectWithSite(iface); ++ return NewMenu_ExtInit_Release(&This->IShellExtInit_iface); ++} ++ ++static HRESULT WINAPI ++NewMenu_ObjectWithSite_GetSite(IObjectWithSite *iface, REFIID iid, void **ppv) ++{ ++ NewMenuImpl *This = impl_from_IObjectWithSite(iface); ++ ++ TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), ppv); ++ ++ if (!This->site) ++ return E_FAIL; ++ ++ return IUnknown_QueryInterface(This->site, iid, ppv); ++} ++ ++static HRESULT WINAPI ++NewMenu_ObjectWithSite_SetSite(IObjectWithSite *iface, IUnknown *punk) ++{ ++ NewMenuImpl *This = impl_from_IObjectWithSite(iface); ++ ++ TRACE("(%p)->(%p)\n", This, punk); ++ ++ if (punk) ++ IUnknown_AddRef(punk); ++ ++ if (This->site) ++ IUnknown_Release(This->site); ++ ++ This->site = punk; ++ return S_OK; ++} ++ ++static const IObjectWithSiteVtbl owsvt = ++{ ++ NewMenu_ObjectWithSite_QueryInterface, ++ NewMenu_ObjectWithSite_AddRef, ++ NewMenu_ObjectWithSite_Release, ++ NewMenu_ObjectWithSite_SetSite, ++ NewMenu_ObjectWithSite_GetSite, ++}; ++ ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_QueryInterface(IContextMenu3 *iface, REFIID riid, void **ppv) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ return NewMenu_ExtInit_QueryInterface(&This->IShellExtInit_iface, riid, ppv); ++} ++ ++static ULONG WINAPI ++NewMenu_ContextMenu3_AddRef(IContextMenu3 *iface) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ return NewMenu_ExtInit_AddRef(&This->IShellExtInit_iface); ++} ++ ++static ULONG WINAPI ++NewMenu_ContextMenu3_Release(IContextMenu3 *iface) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ return NewMenu_ExtInit_Release(&This->IShellExtInit_iface); ++} ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_GetCommandString(IContextMenu3 *iface, UINT_PTR cmd, UINT type, ++ UINT *reserved, LPSTR name, UINT max_len) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ ++ FIXME("(%p)->(%Iu %u %p %p %u): stub\n", This, cmd, type, reserved, name, max_len); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT create_folder(NewMenuImpl *This, IShellView *view) ++{ ++ IFolderView *folder_view = NULL; ++ IShellFolder *desktop = NULL; ++ IShellFolder *parent = NULL; ++ ISFHelper *helper = NULL; ++ LPITEMIDLIST pidl = NULL; ++ WCHAR nameW[MAX_PATH]; ++ HRESULT hr; ++ ++ if (view) ++ { ++ hr = IShellView_QueryInterface(view, &IID_IFolderView, (void **)&folder_view); ++ if (FAILED(hr)) return hr; ++ ++ hr = IFolderView_GetFolder(folder_view, &IID_IShellFolder, (void **)&parent); ++ if (FAILED(hr)) goto out; ++ } ++ else ++ { ++ hr = SHGetDesktopFolder(&desktop); ++ if (FAILED(hr)) goto out; ++ ++ hr = IShellFolder_BindToObject(desktop, This->pidl, NULL, &IID_IShellFolder, (void **)&parent); ++ if (FAILED(hr)) goto out; ++ } ++ ++ IShellFolder_QueryInterface(parent, &IID_ISFHelper, (void **)&helper); ++ if (FAILED(hr)) goto out; ++ ++ hr = ISFHelper_GetUniqueName(helper, nameW, MAX_PATH); ++ if (FAILED(hr)) goto out; ++ ++ hr = ISFHelper_AddFolder(helper, 0, nameW, &pidl); ++ if (FAILED(hr)) goto out; ++ ++ if (view) ++ { ++ IShellView_SelectItem(view, pidl, SVSI_DESELECTOTHERS | SVSI_EDIT | ++ SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT); ++ } ++ ++out: ++ if (pidl) SHFree(pidl); ++ if (helper) ISFHelper_Release(helper); ++ if (parent) IShellFolder_Release(parent); ++ if (desktop) IShellFolder_Release(desktop); ++ if (folder_view) IFolderView_Release(folder_view); ++ return hr; ++} ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_InvokeCommand(IContextMenu3 *iface, LPCMINVOKECOMMANDINFO info) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ IShellBrowser *browser; ++ IShellView *view = NULL; ++ HRESULT hr = E_FAIL; ++ ++ TRACE("(%p)->(%p)\n", This, info); ++ ++ /* New Folder */ ++ if (info->lpVerb == 0) ++ { ++ if ((browser = (IShellBrowser *)SendMessageA(info->hwnd, CWM_GETISHELLBROWSER, 0, 0))) ++ { ++ if (FAILED(IShellBrowser_QueryActiveShellView(browser, &view))) ++ view = NULL; ++ } ++ hr = create_folder(This, view); ++ if (view) IShellView_Release(view); ++ } ++ ++ return hr; ++} ++ ++static UINT insert_new_menu_items(NewMenuImpl *This, HMENU menu, UINT pos, UINT cmd_first, UINT cmd_last) ++{ ++ MENUITEMINFOW item; ++ WCHAR buffer[256]; ++ ++ memset(&item, 0, sizeof(item)); ++ item.cbSize = sizeof(item); ++ ++ if (cmd_first > cmd_last) ++ return cmd_first; ++ ++ /* FIXME: on windows it is only 'Folder' not 'New Folder' */ ++ if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, buffer, sizeof(buffer) / sizeof(WCHAR))) ++ buffer[0] = 0; ++ ++ item.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING; ++ item.dwTypeData = buffer; ++ item.cch = wcslen(buffer); ++ item.wID = cmd_first; ++ item.hbmpItem = HBMMENU_CALLBACK; ++ if (InsertMenuItemW(menu, pos, TRUE, &item)) ++ { ++ This->folder_cmd = cmd_first++; ++ pos++; ++ } ++ ++ return cmd_first; ++} ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_QueryContextMenu(IContextMenu3 *iface, HMENU menu, UINT index, ++ UINT cmd_first, UINT cmd_last, UINT flags) ++{ ++ static WCHAR newW[] = {'N','e','w',0}; ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ MENUITEMINFOW item; ++ HMENU submenu; ++ UINT id; ++ ++ TRACE("(%p)->(%p, %u, %u, %u, %u)\n", This, ++ menu, index, cmd_first, cmd_last, flags ); ++ ++ if (!This->pidl) ++ return E_FAIL; ++ ++ submenu = CreateMenu(); ++ if (!submenu) return E_FAIL; ++ ++ id = insert_new_menu_items(This, submenu, 0, cmd_first, cmd_last); ++ ++ memset(&item, 0, sizeof(item)); ++ item.cbSize = sizeof(item); ++ item.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU; ++ item.fType = MFT_STRING; ++ item.wID = -1; ++ item.dwTypeData = newW; /* FIXME: load from resource file */ ++ item.cch = wcslen(newW); ++ item.fState = MFS_ENABLED; ++ item.hSubMenu = submenu; ++ ++ if (!InsertMenuItemW(menu, index, TRUE, &item)) ++ return E_FAIL; ++ ++ return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id); ++} ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_HandleMenuMsg2(IContextMenu3 *iface, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *result) ++{ ++ NewMenuImpl *This = impl_from_IContextMenu3(iface); ++ ++ TRACE("(%p)->(%u, %Ix, %Ix, %p)\n", This, uMsg, wParam, lParam, result); ++ ++ switch (uMsg) ++ { ++ case WM_MEASUREITEM: ++ { ++ MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; ++ if (!mis || mis->CtlType != ODT_MENU) ++ break; ++ ++ if (This->folder_cmd == mis->itemID) ++ { ++ mis->itemWidth = GetSystemMetrics(SM_CXSMICON); ++ mis->itemHeight = GetSystemMetrics(SM_CYSMICON); ++ } ++ ++ if (result) *result = TRUE; ++ break; ++ } ++ ++ case WM_DRAWITEM: ++ { ++ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; ++ HICON icon = 0; ++ UINT x, y; ++ ++ if (!dis || dis->CtlType != ODT_MENU) ++ break; ++ ++ if (This->folder_cmd == dis->itemID) ++ icon = This->icon_folder; ++ ++ if (!icon) ++ break; ++ ++ x = (dis->rcItem.right - dis->rcItem.left - GetSystemMetrics(SM_CXSMICON)) / 2; ++ y = (dis->rcItem.bottom - dis->rcItem.top - GetSystemMetrics(SM_CYSMICON)) / 2; ++ DrawStateW(dis->hDC, NULL, NULL, (LPARAM)icon, 0, x, y, 0, 0, DST_ICON | DSS_NORMAL); ++ ++ if (result) *result = TRUE; ++ break; ++ } ++ } ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI ++NewMenu_ContextMenu3_HandleMenuMsg(IContextMenu3 *iface, UINT uMsg, WPARAM wParam, LPARAM lParam) ++{ ++ return NewMenu_ContextMenu3_HandleMenuMsg2(iface, uMsg, wParam, lParam, NULL); ++} ++ ++static const IContextMenu3Vtbl cmvt3 = ++{ ++ NewMenu_ContextMenu3_QueryInterface, ++ NewMenu_ContextMenu3_AddRef, ++ NewMenu_ContextMenu3_Release, ++ NewMenu_ContextMenu3_QueryContextMenu, ++ NewMenu_ContextMenu3_InvokeCommand, ++ NewMenu_ContextMenu3_GetCommandString, ++ NewMenu_ContextMenu3_HandleMenuMsg, ++ NewMenu_ContextMenu3_HandleMenuMsg2 ++}; ++ ++HRESULT WINAPI NewMenu_Constructor(IUnknown *outer, REFIID riid, void **obj) ++{ ++ NewMenuImpl *menu; ++ HRESULT hr; ++ ++ TRACE("outer=%p riid=%s\n", outer, debugstr_guid(riid)); ++ ++ *obj = NULL; ++ ++ if (outer) ++ return CLASS_E_NOAGGREGATION; ++ ++ menu = heap_alloc_zero(sizeof(NewMenuImpl)); ++ if (!menu) return E_OUTOFMEMORY; ++ ++ menu->ref = 1; ++ menu->IShellExtInit_iface.lpVtbl = &eivt; ++ menu->IContextMenu3_iface.lpVtbl = &cmvt3; ++ menu->IObjectWithSite_iface.lpVtbl = &owsvt; ++ ++ TRACE("(%p)\n", menu); ++ ++ hr = IShellExtInit_QueryInterface(&menu->IShellExtInit_iface, riid, obj); ++ IShellExtInit_Release(&menu->IShellExtInit_iface); ++ return hr; ++} +diff --git a/dlls/shell32/shellole.c b/dlls/shell32/shellole.c +index 589e5c5170a..2984e691c17 100644 +--- a/dlls/shell32/shellole.c ++++ b/dlls/shell32/shellole.c +@@ -72,6 +72,7 @@ static const struct { + {&CLSID_MyComputer, ISF_MyComputer_Constructor}, + {&CLSID_MyDocuments, MyDocuments_Constructor}, + {&CLSID_NetworkPlaces, ISF_NetworkPlaces_Constructor}, ++ {&CLSID_NewMenu, NewMenu_Constructor}, + {&CLSID_Printers, Printers_Constructor}, + {&CLSID_QueryAssociations, QueryAssociations_Constructor}, + {&CLSID_RecycleBin, RecycleBin_Constructor}, +diff --git a/dlls/shell32/tests/shlview.c b/dlls/shell32/tests/shlview.c +index a83f3137509..2781c2152f9 100644 +--- a/dlls/shell32/tests/shlview.c ++++ b/dlls/shell32/tests/shlview.c +@@ -1478,7 +1478,6 @@ static void test_newmenu(void) + HRESULT hr; + + hr = CoCreateInstance(&CLSID_NewMenu, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk); +- todo_wine + ok(hr == S_OK, "Failed to create NewMenu object, hr %#lx.\n", hr); + if (hr != S_OK) + { +@@ -1490,6 +1489,14 @@ static void test_newmenu(void) + ok(hr == S_OK, "Failed to get IShellExtInit, hr %#lx.\n", hr); + IUnknown_Release(unk2); + ++ hr = IUnknown_QueryInterface(unk, &IID_IContextMenu, (void **)&unk2); ++ ok(hr == S_OK, "Failed to get IContextMenu, hr %#lx.\n", hr); ++ IUnknown_Release(unk2); ++ ++ hr = IUnknown_QueryInterface(unk, &IID_IContextMenu2, (void **)&unk2); ++ ok(hr == S_OK, "Failed to get IContextMenu2, hr %#lx.\n", hr); ++ IUnknown_Release(unk2); ++ + hr = IUnknown_QueryInterface(unk, &IID_IContextMenu3, (void **)&unk2); + ok(hr == S_OK, "Failed to get IContextMenu3, hr %#lx.\n", hr); + IUnknown_Release(unk2); +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/definition new file mode 100644 index 000000000..36964efc0 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-NewMenu_Interface/definition @@ -0,0 +1 @@ +Fixes: [24812] Implement shell32 NewMenu class with new folder item diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/0001-shell32-Append-.exe-when-registry-lookup-fails-first.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/0001-shell32-Append-.exe-when-registry-lookup-fails-first.patch new file mode 100644 index 000000000..f67f423be --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/0001-shell32-Append-.exe-when-registry-lookup-fails-first.patch @@ -0,0 +1,29 @@ +From 70e0b924b2c89bae3f5723c6397fb00df6283586 Mon Sep 17 00:00:00 2001 +From: Louis Lenders +Date: Thu, 4 Nov 2021 21:01:24 +1100 +Subject: [PATCH] shell32: Append .exe when registry lookup fails first time + +--- + dlls/shell32/shlexec.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c +index d24b597181f..674e1e3b0c4 100644 +--- a/dlls/shell32/shlexec.c ++++ b/dlls/shell32/shlexec.c +@@ -437,8 +437,10 @@ + + wcscat(buffer, szName); + res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); +- if (res) goto end; +- ++ if (res) ++ res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wcscpy(buffer, L".exe"), 0, KEY_READ, &hkApp); ++ if(res) ++ goto end; + len = MAX_PATH*sizeof(WCHAR); + res = RegQueryValueW(hkApp, NULL, lpResult, &len); + if (res) goto end; +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/definition new file mode 100644 index 000000000..e01eea197 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/shell32-registry-lookup-app/definition @@ -0,0 +1 @@ +Fixes: [51957] Append .exe, if registry lookup fails first. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/0001-user32-Improve-FlashWindowEx-message-and-return-valu.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/0001-user32-Improve-FlashWindowEx-message-and-return-valu.patch new file mode 100644 index 000000000..a8867291e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/0001-user32-Improve-FlashWindowEx-message-and-return-valu.patch @@ -0,0 +1,59 @@ +From a295be388db4bbe50867b295d09a57726321261c Mon Sep 17 00:00:00 2001 +From: James Coonradt +Date: Tue, 19 Sep 2017 12:28:50 -0600 +Subject: [PATCH] user32: Improve FlashWindowEx message and return value. + +--- + dlls/user32/tests/win.c | 4 ++-- + dlls/user32/win.c | 1 - + dlls/win32u/window.c | 5 ++--- + 3 files changed, 4 insertions(+), 6 deletions(-) + +diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c +index 496c3f9ab07..2718d4c96c9 100644 +--- a/dlls/user32/tests/win.c ++++ b/dlls/user32/tests/win.c +@@ -9825,7 +9825,7 @@ static void test_FlashWindowEx(void) + + SetLastError(0xdeadbeef); + ret = pFlashWindowEx(&finfo); +- todo_wine ok(!ret, "previous window state should not be active\n"); ++ ok(!ret, "previous window state should not be active\n"); + + finfo.cbSize = sizeof(FLASHWINFO) - 1; + SetLastError(0xdeadbeef); +@@ -9876,7 +9876,7 @@ static void test_FlashWindowEx(void) + finfo.dwFlags = FLASHW_STOP; + SetLastError(0xdeadbeef); + ret = pFlashWindowEx(&finfo); +- ok(prev != ret, "previous window state should be different\n"); ++ todo_wine ok(prev != ret, "previous window state should be different\n"); + + DestroyWindow( hwnd ); + } +diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c +index 70f73cb0fb3..e677e21af5d 100644 +--- a/dlls/win32u/window.c ++++ b/dlls/win32u/window.c +@@ -4551,8 +4551,7 @@ BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info ) + if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE; + hwnd = win->obj.handle; /* make it a full handle */ + +- if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED); +- else wparam = (hwnd == NtUserGetForegroundWindow()); ++ wparam = (win->flags & WIN_NCACTIVATED) != 0; + + release_win_ptr( win ); + +@@ -4560,7 +4559,7 @@ BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info ) + send_notify_message( hwnd, WM_NCACTIVATE, wparam, 0, 0 ); + + user_driver->pFlashWindowEx( info ); +- return wparam; ++ return (info->dwFlags & FLASHW_CAPTION) ? TRUE : wparam; + } + } + +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/definition new file mode 100644 index 000000000..2cb723511 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/user32-FlashWindowEx/definition @@ -0,0 +1 @@ +Fixes: [43124] FlashWindowEx: WM_NCACTIVATE behavior is incorrect diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch new file mode 100644 index 000000000..a22ea977b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0001-include-Add-windows.networking.connectivity.idl.patch @@ -0,0 +1,400 @@ +From a5d61672e671ca208fb51fd227566179128bc888 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Fri, 28 May 2021 12:34:37 +1000 +Subject: [PATCH] include: Add windows.networking.connectivity.idl + +--- + include/Makefile.in | 1 + + include/windows.networking.connectivity.idl | 368 ++++++++++++++++++++ + 2 files changed, 369 insertions(+) + create mode 100644 include/windows.networking.connectivity.idl + +diff --git a/include/Makefile.in b/include/Makefile.in +index a1bf9fbd594..e379070caa9 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -790,6 +790,7 @@ SOURCES = \ + windows.media.devices.idl \ + windows.media.idl \ + windows.media.speechsynthesis.idl \ ++ windows.networking.connectivity.idl \ + windows.security.cryptography.idl \ + windows.security.exchangeactivesyncprovisioning.idl \ + windows.storage.streams.idl \ +diff --git a/include/windows.networking.connectivity.idl b/include/windows.networking.connectivity.idl +new file mode 100644 +index 00000000000..3ccefca02fa +--- /dev/null ++++ b/include/windows.networking.connectivity.idl +@@ -0,0 +1,368 @@ ++/* ++ * Copyright 2021 Alistair Leslie-Hughes ++ * ++ * 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 ++ */ ++ ++#ifdef __WIDL__ ++#pragma winrt ns_prefix ++#endif ++ ++import "inspectable.idl"; ++import "asyncinfo.idl"; ++import "eventtoken.idl"; ++import "windowscontracts.idl"; ++import "windows.foundation.idl"; ++ ++namespace Windows ++{ ++ namespace Foundation ++ { ++ interface IClosable; ++ ++ /*runtimeclass Uri;*/ ++ } ++} ++ ++namespace Windows ++{ ++ namespace Networking ++ { ++ /*typedef enum DomainNameType DomainNameType;*/ ++ ++ /*runtimeclass EndpointPair;*/ ++ ++ /*runtimeclass HostName;*/ ++ ++ /*typedef enum HostNameSortOptions HostNameSortOptions;*/ ++ } ++} ++ ++namespace Windows ++{ ++ namespace Networking ++ { ++ namespace Connectivity ++ { ++ runtimeclass ConnectionCost; ++ runtimeclass ConnectionProfile; ++ runtimeclass DataPlanStatus; ++ runtimeclass DataPlanUsage; ++ runtimeclass DataUsage; ++ runtimeclass IPInformation; ++ runtimeclass LanIdentifier; ++ runtimeclass LanIdentifierData; ++ runtimeclass NetworkAdapter; ++ runtimeclass NetworkInformation; ++ runtimeclass NetworkSecuritySettings; ++ runtimeclass ProxyConfiguration; ++ ++ typedef enum NetworkConnectivityLevel NetworkConnectivityLevel; ++ typedef enum NetworkCostType NetworkCostType; ++ typedef enum RoamingStates RoamingStates; ++ typedef enum NetworkAuthenticationType NetworkAuthenticationType; ++ typedef enum NetworkEncryptionType NetworkEncryptionType; ++ ++ declare ++ { ++ interface Windows.Foundation.Collections.IVectorView; ++ interface Windows.Foundation.Collections.IVectorView; ++ /*interface Windows.Foundation.Collections.IVectorView;*/ ++ /*interface Windows.Foundation.Collections.IVectorView;*/ ++ ++ ++ /*interface Windows.Foundation.Collections.IIterable;*/ ++ ++ /*interface Windows.Foundation.IAsyncOperation;*/ ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ enum NetworkConnectivityLevel ++ { ++ None, ++ LocalAccess, ++ ConstrainedInternetAccess, ++ InternetAccess ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ enum NetworkCostType ++ { ++ Unknown, ++ Unrestricted, ++ Fixed, ++ Variable ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [flags] ++ enum RoamingStates ++ { ++ None = 0x0, ++ NotRoaming = 0x1, ++ Roaming = 0x2 ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ enum NetworkAuthenticationType ++ { ++ None, ++ Unknown, ++ Open80211, ++ SharedKey80211, ++ Wpa, ++ WpaPsk, ++ WpaNone, ++ Rsna, ++ RsnaPsk, ++ Ihv ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ enum NetworkEncryptionType ++ { ++ None, ++ Unknown, ++ Wep, ++ Wep40, ++ Wep104, ++ Tkip, ++ Ccmp, ++ WpaUseGroup, ++ RsnUseGroup, ++ Ihv ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [uuid(71ba143f-598e-49d0-84eb-8febaedcc195)] ++ delegate HRESULT NetworkStatusChangedEventHandler([in] IInspectable* sender); ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.ConnectionProfile)] ++ [uuid(71ba143c-598e-49d0-84eb-8febaedcc195)] ++ interface IConnectionProfile : IInspectable ++ { ++ [propget] HRESULT ProfileName([out, retval] HSTRING* value); ++ HRESULT GetNetworkConnectivityLevel([out, retval] Windows.Networking.Connectivity.NetworkConnectivityLevel* value); ++ HRESULT GetNetworkNames([out, retval] Windows.Foundation.Collections.IVectorView** value); ++ HRESULT GetConnectionCost([out, retval] Windows.Networking.Connectivity.ConnectionCost** value); ++ HRESULT GetDataPlanStatus([out, retval] Windows.Networking.Connectivity.DataPlanStatus** value); ++ [propget] HRESULT NetworkAdapter([out, retval] Windows.Networking.Connectivity.NetworkAdapter** value); ++ HRESULT GetLocalUsage([in] Windows.Foundation.DateTime start, [in] Windows.Foundation.DateTime end, ++ [out, retval] Windows.Networking.Connectivity.DataUsage** value); ++ HRESULT GetLocalUsagePerRoamingStates([in] Windows.Foundation.DateTime start, ++ [in] Windows.Foundation.DateTime end, [in] Windows.Networking.Connectivity.RoamingStates states, ++ [out, retval] Windows.Networking.Connectivity.DataUsage** value); ++ [propget] HRESULT NetworkSecuritySettings([out, retval] Windows.Networking.Connectivity.NetworkSecuritySettings** value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.LanIdentifier)] ++ [uuid(48aa53aa-1108-4546-a6cb-9a74da4b7ba0)] ++ interface ILanIdentifier : IInspectable ++ { ++ [propget] HRESULT InfrastructureId([out, retval] Windows.Networking.Connectivity.LanIdentifierData** value); ++ [propget] HRESULT PortId([out, retval] Windows.Networking.Connectivity.LanIdentifierData** value); ++ [propget] HRESULT NetworkAdapterId([out, retval] GUID* value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.LanIdentifierData)] ++ [uuid(a74e83c3-d639-45be-a36a-c4e4aeaf6d9b)] ++ interface ILanIdentifierData : IInspectable ++ { ++ [propget] HRESULT Type([out, retval] UINT32* value); ++ [propget] HRESULT Value([out,retval] /*Windows.Foundation.Collections.IVectorView** */ BYTE **value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.IPInformation)] ++ [uuid(d85145e0-138f-47d7-9b3a-36bb488cef33)] ++ interface IIPInformation : IInspectable ++ { ++ [propget] HRESULT NetworkAdapter([out, retval] Windows.Networking.Connectivity.NetworkAdapter** value); ++ [propget] HRESULT PrefixLength([out, retval] /*Windows.Foundation.IReference** */ BYTE **value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.NetworkInformation)] ++ [uuid(5074f851-950d-4165-9c15-365619481eea)] ++ interface INetworkInformationStatics : IInspectable ++ { ++ HRESULT GetConnectionProfiles([out, retval] Windows.Foundation.Collections.IVectorView** value); ++ HRESULT GetInternetConnectionProfile([out, retval] Windows.Networking.Connectivity.ConnectionProfile** value); ++ HRESULT GetLanIdentifiers([out, retval] Windows.Foundation.Collections.IVectorView** value); ++ HRESULT GetHostNames([out, retval] /*Windows.Foundation.Collections.IVectorView** */ DWORD **value); ++ HRESULT GetProxyConfigurationAsync(/*[in] Windows.Foundation.Uri* */ char* uri, ++ [out, retval] /*Windows.Foundation.IAsyncOperation** */ DWORD **value); ++ HRESULT GetSortedEndpointPairs([in] /* Windows.Foundation.Collections.IIterable* */ DWORD *endpoint, ++ [in] /*Windows.Networking.HostNameSortOptions*/ DWORD options, ++ [out, retval] /*Windows.Foundation.Collections.IVectorView** */ DWORD **value); ++ [eventadd] HRESULT NetworkStatusChanged([in] Windows.Networking.Connectivity.NetworkStatusChangedEventHandler* handler, ++ [out, retval] EventRegistrationToken* eventCookie); ++ [eventremove] HRESULT NetworkStatusChanged([in] EventRegistrationToken cookie); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.ConnectionCost)] ++ [uuid(bad7d829-3416-4b10-a202-bac0b075bdae)] ++ interface IConnectionCost : IInspectable ++ { ++ [propget] HRESULT NetworkCostType([out, retval] Windows.Networking.Connectivity.NetworkCostType* value); ++ [propget] HRESULT Roaming([out, retval] boolean* value); ++ [propget] HRESULT OverDataLimit([out, retval] boolean* value); ++ [propget] HRESULT ApproachingDataLimit([out, retval] boolean* value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.DataUsage)] ++ [uuid(c1431dd3-b146-4d39-b959-0c69b096c512)] ++ interface IDataUsage : IInspectable ++ { ++ [propget] HRESULT BytesSent([out, retval] UINT64* value); ++ [propget] HRESULT BytesReceived([out, retval] UINT64* value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.NetworkAdapter)] ++ [uuid(3b542e03-5388-496c-a8a3-affd39aec2e6)] ++ interface INetworkAdapter : IInspectable ++ { ++ [propget] HRESULT OutboundMaxBitsPerSecond([out, retval] UINT64* value); ++ [propget] HRESULT InboundMaxBitsPerSecond([out ,retval] UINT64* value); ++ [propget] HRESULT IanaInterfaceType([out, retval] UINT32* value); ++ [propget] HRESULT NetworkItem([out, retval] /*Windows.Networking.Connectivity.NetworkItem** */ DWORD **value); ++ [propget] HRESULT NetworkAdapterId([out, retval] GUID* value); ++ HRESULT GetConnectedProfileAsync([out, retval] /*Windows.Foundation.IAsyncOperation** */ DWORD **value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.NetworkSecuritySettings)] ++ [uuid(7CA07E8D-917B-4B5F-B84D-28F7A5AC5402)] ++ interface INetworkSecuritySettings : IInspectable ++ { ++ [propget] HRESULT NetworkAuthenticationType([out, retval] Windows.Networking.Connectivity.NetworkAuthenticationType* value); ++ [propget] HRESULT NetworkEncryptionType([out, retval] Windows.Networking.Connectivity.NetworkEncryptionType* value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.DataPlanUsage)] ++ [uuid(b921492d-3b44-47ff-b361-be59e69ed1b0)] ++ interface IDataPlanUsage : IInspectable ++ { ++ [propget] HRESULT MegabytesUsed([out] [retval] UINT32* value); ++ [propget] HRESULT LastSyncTime([out] [retval] Windows.Foundation.DateTime* value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.Connectivity.DataPlanStatus)] ++ [uuid(977a8b8c-3885-40f3-8851-42cd2bd568bb)] ++ interface IDataPlanStatus : IInspectable ++ { ++ [propget] HRESULT DataPlanUsage([out, retval] Windows.Networking.Connectivity.DataPlanUsage** value); ++ [propget] HRESULT DataLimitInMegabytes([out, retval] /*Windows.Foundation.IReference** */ UINT32 **value); ++ [propget] HRESULT InboundBitsPerSecond([out, retval] /* Windows.Foundation.IReference** */ UINT64 **value); ++ [propget] HRESULT OutboundBitsPerSecond([out, retval] /* Windows.Foundation.IReference** */ UINT64 **value); ++ [propget] HRESULT NextBillingCycle([out, retval] /* Windows.Foundation.IReference** */ UINT64 **value); ++ [propget] HRESULT MaxTransferSizeInMegabytes([out, retval] /*Windows.Foundation.IReference** */ UINT32 **value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass DataPlanStatus ++ { ++ [default] interface Windows.Networking.Connectivity.IDataPlanStatus; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass DataUsage ++ { ++ [default] interface Windows.Networking.Connectivity.IDataUsage; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass NetworkAdapter ++ { ++ [default] interface Windows.Networking.Connectivity.INetworkAdapter; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass DataPlanUsage ++ { ++ [default] interface Windows.Networking.Connectivity.IDataPlanUsage; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ [static(Windows.Networking.Connectivity.INetworkInformationStatics, Windows.Foundation.UniversalApiContract, 1.0)] ++ /*[static(Windows.Networking.Connectivity.INetworkInformationStatics2, Windows.Foundation.UniversalApiContract, 1.0)]*/ ++ runtimeclass NetworkInformation ++ { ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass IPInformation ++ { ++ [default] interface Windows.Networking.Connectivity.IIPInformation; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass NetworkSecuritySettings ++ { ++ [default] interface Windows.Networking.Connectivity.INetworkSecuritySettings; ++ } ++ ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass ConnectionProfile ++ { ++ [default] interface Windows.Networking.Connectivity.IConnectionProfile; ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] interface Windows.Networking.Connectivity.IConnectionProfile2; ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] interface Windows.Networking.Connectivity.IConnectionProfile3; ++ [contract(Windows.Foundation.UniversalApiContract, 5.0)] interface Windows.Networking.Connectivity.IConnectionProfile4; ++ [contract(Windows.Foundation.UniversalApiContract, 7.0)] interface Windows.Networking.Connectivity.IConnectionProfile5; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass ConnectionCost ++ { ++ [default] interface Windows.Networking.Connectivity.IConnectionCost; ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] interface Windows.Networking.Connectivity.IConnectionCost2; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass LanIdentifier ++ { ++ [default] interface Windows.Networking.Connectivity.ILanIdentifier; ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ runtimeclass LanIdentifierData ++ { ++ [default] interface Windows.Networking.Connectivity.ILanIdentifierData; ++ } ++ ++ } ++ } ++} +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch new file mode 100644 index 000000000..e5445054f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0002-include-Add-windows.networking.idl.patch @@ -0,0 +1,120 @@ +From e98050400335c07a044c28774c802647ff075af5 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 1 Jun 2021 10:26:28 +1000 +Subject: [PATCH] include: Add windows.networking.idl + +--- + include/Makefile.in | 1 + + include/windows.networking.idl | 87 ++++++++++++++++++++++++++++++++++ + 2 files changed, 88 insertions(+) + create mode 100644 include/windows.networking.idl + +diff --git a/include/Makefile.in b/include/Makefile.in +index e379070caa9..043f1436216 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -786,6 +786,7 @@ SOURCES = \ + windows.media.devices.idl \ + windows.media.idl \ + windows.media.speechsynthesis.idl \ ++ windows.networking.idl \ + windows.networking.connectivity.idl \ + windows.security.cryptography.idl \ + windows.security.exchangeactivesyncprovisioning.idl \ +diff --git a/include/windows.networking.idl b/include/windows.networking.idl +new file mode 100644 +index 00000000000..160cd78e540 +--- /dev/null ++++ b/include/windows.networking.idl +@@ -0,0 +1,87 @@ ++/* ++ * Copyright 2021 Alistair Leslie-Hughes ++ * ++ * 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 ++ */ ++ ++#ifdef __WIDL__ ++#pragma winrt ns_prefix ++#endif ++ ++import "inspectable.idl"; ++import "asyncinfo.idl"; ++import "eventtoken.idl"; ++import "windowscontracts.idl"; ++import "windows.foundation.idl"; ++import "windows.networking.connectivity.idl"; ++ ++namespace Windows ++{ ++ namespace Networking ++ { ++ typedef enum HostNameType HostNameType; ++ ++ runtimeclass HostName; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ enum HostNameType ++ { ++ DomainName, ++ Ipv4, ++ Ipv6, ++ Bluetooth ++ }; ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.HostName)] ++ [uuid(bf8ecaad-ed96-49a7-9084-d416cae88dcb)] ++ interface IHostName : IInspectable ++ { ++ [propget] HRESULT IPInformation([out, retval] Windows.Networking.Connectivity.IPInformation** value); ++ [propget] HRESULT RawName([out, retval] HSTRING* value); ++ [propget] HRESULT DisplayName([out, retval] HSTRING* value); ++ [propget] HRESULT CanonicalName([out, retval] HSTRING* value); ++ [propget] HRESULT Type([out, retval] Windows.Networking.HostNameType* value); ++ HRESULT IsEqual([in] Windows.Networking.HostName* hostName, [out, retval] boolean* equal); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.HostName)] ++ [uuid(458c23ed-712f-4576-adf1-c20b2c643558)] ++ interface IHostNameFactory : IInspectable ++ { ++ HRESULT CreateHostName([in] HSTRING hostname, [out, retval] Windows.Networking.HostName** value); ++ } ++ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [exclusiveto(Windows.Networking.HostName)] ++ [uuid(f68cd4bf-a388-4e8b-91ea-54dd6dd901c0)] ++ interface IHostNameStatics : IInspectable ++ { ++ HRESULT Compare([in] HSTRING value1, [in] HSTRING value2, [out, retval] INT32* result); ++ } ++ ++ /*[activatable(Windows.Networking.IHostNameFactory, Windows.Foundation.UniversalApiContract, 1.0)]*/ ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] ++ [marshaling_behavior(agile)] ++ [static(Windows.Networking.IHostNameStatics, Windows.Foundation.UniversalApiContract, 1.0)] ++ [threading(both)] ++ runtimeclass HostName ++ { ++ [default] interface Windows.Networking.IHostName; ++ [contract(Windows.Foundation.UniversalApiContract, 1.0)] interface Windows.Foundation.IStringable; ++ } ++ } ++} +\ No newline at end of file +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch new file mode 100644 index 000000000..c1475f039 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0003-windows.networking.connectivity-Add-stub-dll.patch @@ -0,0 +1,97 @@ +From a446063a84d5b48ac4c8aa05abca98da8a386467 Mon Sep 17 00:00:00 2001 +From: Esdras Tarsis +Date: Wed, 2 Sep 2020 23:41:19 -0300 +Subject: [PATCH 3/8] windows.networking.connectivity: Add stub dll. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46534 +Signed-off-by: Esdras Tarsis +--- + configure.ac | 1 + + .../Makefile.in | 7 ++++ + .../windows.networking.connectivity.spec | 3 ++ + .../windows.networking.connectivity_main.c | 35 +++++++++++++++++++ + 4 files changed, 46 insertions(+) + create mode 100644 dlls/windows.networking.connectivity/Makefile.in + create mode 100644 dlls/windows.networking.connectivity/windows.networking.connectivity.spec + create mode 100644 dlls/windows.networking.connectivity/windows.networking.connectivity_main.c + +diff --git a/configure.ac b/configure.ac +index 7ea0d824cee..e8d0d8a9023 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3775,6 +3775,7 @@ WINE_CONFIG_MAKEFILE(dlls/windows.media.devices) + WINE_CONFIG_MAKEFILE(dlls/windows.media.speech) + WINE_CONFIG_MAKEFILE(dlls/windows.media.speech/tests) + WINE_CONFIG_MAKEFILE(dlls/windows.networking) ++WINE_CONFIG_MAKEFILE(dlls/windows.networking.connectivity) + WINE_CONFIG_MAKEFILE(dlls/windowscodecs) + WINE_CONFIG_MAKEFILE(dlls/windowscodecs/tests) + WINE_CONFIG_MAKEFILE(dlls/windowscodecsext) +diff --git a/dlls/windows.networking.connectivity/Makefile.in b/dlls/windows.networking.connectivity/Makefile.in +new file mode 100644 +index 00000000000..6fc24a72feb +--- /dev/null ++++ b/dlls/windows.networking.connectivity/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = windows.networking.connectivity ++IMPORTS = combase uuid ++ ++EXTRADLLFLAGS = -mno-cygwin ++ ++C_SRCS = \ ++ windows.networking.connectivity_main.c +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity.spec b/dlls/windows.networking.connectivity/windows.networking.connectivity.spec +new file mode 100644 +index 00000000000..4b286869e02 +--- /dev/null ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity.spec +@@ -0,0 +1,3 @@ ++@ stdcall -private DllCanUnloadNow() ++@ stdcall -private DllGetActivationFactory(ptr ptr) ++@ stdcall -private DllGetClassObject(ptr ptr ptr) +\ No newline at end of file +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +new file mode 100644 +index 00000000000..96ff0ea5af2 +--- /dev/null ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +@@ -0,0 +1,35 @@ ++#include ++ ++#define COBJMACROS ++#include "windef.h" ++#include "winbase.h" ++#include "winstring.h" ++#include "wine/debug.h" ++ ++#include "objbase.h" ++#include "initguid.h" ++ ++#include "activation.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(network); ++ ++static const char *debugstr_hstring(HSTRING hstr) ++{ ++ const WCHAR *str; ++ UINT32 len; ++ if (hstr && !((ULONG_PTR)hstr >> 16)) return "(invalid)"; ++ str = WindowsGetStringRawBuffer(hstr, &len); ++ return wine_dbgstr_wn(str, len); ++} ++ ++HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, LPVOID *object) ++{ ++ FIXME("clsid %s, riid %s, object %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), object); ++ return CLASS_E_CLASSNOTAVAILABLE; ++} ++ ++HRESULT WINAPI DllGetActivationFactory(HSTRING classid, IActivationFactory **factory) ++{ ++ FIXME("classid %s, factory %p stub!\n", debugstr_hstring(classid), factory); ++ return E_NOINTERFACE; ++} +\ No newline at end of file +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch new file mode 100644 index 000000000..9ac057ea2 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0004-windows.networking.connectivity-Implement-IActivatio.patch @@ -0,0 +1,136 @@ +From 587e8baeef029940518223d13b754b4e6c0352cd Mon Sep 17 00:00:00 2001 +From: Esdras Tarsis +Date: Wed, 2 Sep 2020 23:53:28 -0300 +Subject: [PATCH 4/8] windows.networking.connectivity: Implement + IActivationFactory stubs. + +v2: +Add Proper QueryInterface +Fixed Initial Reference Count value. +--- + .../windows.networking.connectivity_main.c | 104 +++++++++++++++++- + 1 file changed, 101 insertions(+), 3 deletions(-) + +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +index 96ff0ea5af2..2463cc2c93c 100644 +--- a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +@@ -22,6 +22,102 @@ static const char *debugstr_hstring(HSTRING hstr) + return wine_dbgstr_wn(str, len); + } + ++struct windows_networking_connectivity ++{ ++ IActivationFactory IActivationFactory_iface; ++ LONG refcount; ++}; ++ ++static inline struct windows_networking_connectivity *impl_from_IActivationFactory(IActivationFactory *iface) ++{ ++ return CONTAINING_RECORD(iface, struct windows_networking_connectivity, IActivationFactory_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_QueryInterface( ++ IActivationFactory *iface, REFIID iid, void **object) ++{ ++ TRACE("iface %p, iid %s, object %p stub!\n", iface, debugstr_guid(iid), object); ++ ++ if (IsEqualGUID(iid, &IID_IUnknown) || ++ IsEqualGUID(iid, &IID_IInspectable) || ++ IsEqualGUID(iid, &IID_IAgileObject) || ++ IsEqualGUID(iid, &IID_IActivationFactory)) ++ { ++ IUnknown_AddRef(iface); ++ *object = &impl->INetworkInformationStatics_iface; ++ return S_OK; ++ } ++ ++ FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE windows_networking_connectivity_AddRef( ++ IActivationFactory *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_IActivationFactory(iface); ++ ULONG rc = InterlockedIncrement(&impl->refcount); ++ TRACE("%p increasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static ULONG STDMETHODCALLTYPE windows_networking_connectivity_Release( ++ IActivationFactory *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_IActivationFactory(iface); ++ ULONG rc = InterlockedDecrement(&impl->refcount); ++ TRACE("%p decreasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_GetIids( ++ IActivationFactory *iface, ULONG *iid_count, IID **iids) ++{ ++ FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_GetRuntimeClassName( ++ IActivationFactory *iface, HSTRING *class_name) ++{ ++ FIXME("iface %p, class_name %p stub!\n", iface, class_name); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_GetTrustLevel( ++ IActivationFactory *iface, TrustLevel *trust_level) ++{ ++ FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_ActivateInstance( ++ IActivationFactory *iface, IInspectable **instance) ++{ ++ FIXME("iface %p, instance %p stub!\n", iface, instance); ++ return E_NOTIMPL; ++} ++ ++static const struct IActivationFactoryVtbl activation_factory_vtbl = ++{ ++ windows_networking_connectivity_QueryInterface, ++ windows_networking_connectivity_AddRef, ++ windows_networking_connectivity_Release, ++ /* IInspectable methods */ ++ windows_networking_connectivity_GetIids, ++ windows_networking_connectivity_GetRuntimeClassName, ++ windows_networking_connectivity_GetTrustLevel, ++ /* IActivationFactory methods */ ++ windows_networking_connectivity_ActivateInstance, ++}; ++ ++static struct windows_networking_connectivity windows_networking_connectivity = ++{ ++ {&activation_factory_vtbl}, ++ 1 ++}; ++ + HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, LPVOID *object) + { + FIXME("clsid %s, riid %s, object %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), object); +@@ -30,6 +126,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, LPVOID *object) + + HRESULT WINAPI DllGetActivationFactory(HSTRING classid, IActivationFactory **factory) + { +- FIXME("classid %s, factory %p stub!\n", debugstr_hstring(classid), factory); +- return E_NOINTERFACE; +-} +\ No newline at end of file ++ TRACE("classid %s, factory %p.\n", debugstr_hstring(classid), factory); ++ *factory = &windows_networking_connectivity.IActivationFactory_iface; ++ IUnknown_AddRef(*factory); ++ return S_OK; ++} +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch new file mode 100644 index 000000000..b7f03889a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0005-windows.networking.connectivity-Implement-INetworkIn.patch @@ -0,0 +1,301 @@ +From bb14d124f11785e8abd8abdbc0a15854d88848ce Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 1 Jun 2021 11:10:25 +1000 +Subject: [PATCH 5/8] windows.networking.connectivity: Implement + INetworkInformationStatics stubs. + +v2: +Fixed QueryInterfaces + +Based off patch by Esdras Tarsis. +--- + .../windows.networking.connectivity_main.c | 242 ++++++++++++++++++ + 1 file changed, 242 insertions(+) + +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +index 2463cc2c93c..b96e6c4f0a4 100644 +--- a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +@@ -10,6 +10,9 @@ + #include "initguid.h" + + #include "activation.h" ++#define WIDL_using_Windows_Networking_Connectivity ++#define WIDL_using_Windows_Foundation_Collections ++#include "windows.networking.connectivity.h" + + WINE_DEFAULT_DEBUG_CHANNEL(network); + +@@ -25,6 +28,8 @@ static const char *debugstr_hstring(HSTRING hstr) + struct windows_networking_connectivity + { + IActivationFactory IActivationFactory_iface; ++ INetworkInformationStatics INetworkInformationStatics_iface; ++ IVectorView_ConnectionProfile IVectorView_iface; + LONG refcount; + }; + +@@ -33,9 +38,237 @@ static inline struct windows_networking_connectivity *impl_from_IActivationFacto + return CONTAINING_RECORD(iface, struct windows_networking_connectivity, IActivationFactory_iface); + } + ++static inline struct windows_networking_connectivity *impl_from_INetworkInformationStatics(INetworkInformationStatics *iface) ++{ ++ return CONTAINING_RECORD(iface, struct windows_networking_connectivity, INetworkInformationStatics_iface); ++} ++ ++static inline struct windows_networking_connectivity *impl_from_IVectorView_ConnectionProfile(IVectorView_ConnectionProfile *iface) ++{ ++ return CONTAINING_RECORD(iface, struct windows_networking_connectivity, IVectorView_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_QueryInterface(IVectorView_ConnectionProfile *iface, REFIID iid, void **object) ++{ ++ TRACE("iface %p, iid %s, object %p stub!\n", iface, debugstr_guid(iid), object); ++ IUnknown_AddRef(iface); ++ *object = iface; ++ return S_OK; ++} ++ ++static ULONG STDMETHODCALLTYPE vector_view_AddRef(IVectorView_ConnectionProfile *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_IVectorView_ConnectionProfile(iface); ++ ULONG rc = InterlockedIncrement(&impl->refcount); ++ TRACE("%p increasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static ULONG STDMETHODCALLTYPE vector_view_Release(IVectorView_ConnectionProfile *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_IVectorView_ConnectionProfile(iface); ++ ULONG rc = InterlockedDecrement(&impl->refcount); ++ TRACE("%p decreasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_GetIids(IVectorView_ConnectionProfile *iface, ULONG *iid_count, IID **iids) ++{ ++ FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_GetRuntimeClassName(IVectorView_ConnectionProfile *iface, HSTRING *class_name) ++{ ++ FIXME("iface %p, class_name %p stub!\n", iface, class_name); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_GetTrustLevel(IVectorView_ConnectionProfile *iface, TrustLevel *trust_level) ++{ ++ FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_GetAt(IVectorView_ConnectionProfile *iface, ULONG index, IConnectionProfile **out_value) ++{ ++ FIXME("iface %p, index %#x, out_value %p stub!\n", iface, index, out_value); ++ return S_OK; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_get_Size( ++ IVectorView_ConnectionProfile *iface, ULONG *out_value) ++{ ++ FIXME("iface %p, out_value %p stub!\n", iface, out_value); ++ *out_value = 0; ++ return S_OK; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_IndexOf( ++ IVectorView_ConnectionProfile *iface, IConnectionProfile *value, ULONG *index, BOOLEAN *out_value) ++{ ++ FIXME("iface %p, value %p, index %p, out_value %p stub!\n", iface, value, index, out_value); ++ *out_value = FALSE; ++ return S_OK; ++} ++ ++static HRESULT STDMETHODCALLTYPE vector_view_GetMany( ++ IVectorView_ConnectionProfile *iface, UINT32 start_index, UINT32 size, IConnectionProfile **items, UINT32 *out_value) ++{ ++ FIXME("iface %p, start_index %#x, items %p, out_value %p stub!\n", iface, start_index, items, out_value); ++ *out_value = 0; ++ return S_OK; ++} ++ ++static const struct IVectorView_ConnectionProfileVtbl vector_view_vtbl = ++{ ++ vector_view_QueryInterface, ++ vector_view_AddRef, ++ vector_view_Release, ++ vector_view_GetIids, ++ vector_view_GetRuntimeClassName, ++ vector_view_GetTrustLevel, ++ vector_view_GetAt, ++ vector_view_get_Size, ++ vector_view_IndexOf, ++ vector_view_GetMany ++}; ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_QueryInterface( ++ INetworkInformationStatics *iface, REFIID iid, void **object) ++{ ++ TRACE("iface %p, iid %s, object %p stub!\n", iface, debugstr_guid(iid), object); ++ ++ if (IsEqualGUID(iid, &IID_IUnknown) || ++ IsEqualGUID(iid, &IID_IInspectable) || ++ IsEqualGUID(iid, &IID_IAgileObject) || ++ IsEqualGUID(iid, &IID_INetworkInformationStatics)) ++ { ++ IUnknown_AddRef(iface); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE network_information_statics_AddRef( ++ INetworkInformationStatics *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_INetworkInformationStatics(iface); ++ ULONG rc = InterlockedIncrement(&impl->refcount); ++ TRACE("%p increasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static ULONG STDMETHODCALLTYPE network_information_statics_Release( ++ INetworkInformationStatics *iface) ++{ ++ struct windows_networking_connectivity *impl = impl_from_INetworkInformationStatics(iface); ++ ULONG rc = InterlockedDecrement(&impl->refcount); ++ TRACE("%p decreasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetIids( ++ INetworkInformationStatics *iface, ULONG *iid_count, IID **iids) ++{ ++ FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetRuntimeClassName( ++ INetworkInformationStatics *iface, HSTRING *class_name) ++{ ++ FIXME("iface %p, class_name %p stub!\n", iface, class_name); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetTrustLevel( ++ INetworkInformationStatics *iface, TrustLevel *trust_level) ++{ ++ FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetConnectionProfiles(INetworkInformationStatics *iface, __FIVectorView_1_Windows__CNetworking__CConnectivity__CConnectionProfile **value) ++{ ++ struct windows_networking_connectivity *impl = impl_from_INetworkInformationStatics(iface); ++ FIXME("iface %p, %p stub!\n", iface, value); ++ *value = &impl->IVectorView_iface; ++ return S_OK; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetInternetConnectionProfile(INetworkInformationStatics *iface, IConnectionProfile **value) ++{ ++ FIXME("iface %p, %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetLanIdentifiers(INetworkInformationStatics *iface, __FIVectorView_1_Windows__CNetworking__CConnectivity__CLanIdentifier **value) ++{ ++ FIXME("iface %p, %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetHostNames(INetworkInformationStatics *iface, DWORD **value) ++{ ++ FIXME("iface %p, %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetProxyConfigurationAsync(INetworkInformationStatics *iface, char *name, DWORD **value) ++{ ++ FIXME("iface %p, %p, %p stub!\n", iface, name, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetSortedEndpointPairs(INetworkInformationStatics *iface, DWORD* destinationList, DWORD sortOptions, DWORD **value) ++{ ++ FIXME("iface %p, %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_eventadd_NetworkStatusChanged( ++ INetworkInformationStatics *iface, __x_ABI_CWindows_CNetworking_CConnectivity_CINetworkStatusChangedEventHandler *value, EventRegistrationToken* token) ++{ ++ FIXME("iface %p, value %p, token %p stub!\n", iface, value, token); ++ return S_OK; ++} ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_eventremove_NetworkStatusChanged( ++ INetworkInformationStatics *iface, EventRegistrationToken token) ++{ ++ FIXME("iface %p, token %#I64x stub!\n", iface, token.value); ++ return S_OK; ++} ++ ++static const struct INetworkInformationStaticsVtbl network_information_statics_vtbl = ++{ ++ network_information_statics_QueryInterface, ++ network_information_statics_AddRef, ++ network_information_statics_Release, ++ /* IInspectable methods */ ++ network_information_statics_GetIids, ++ network_information_statics_GetRuntimeClassName, ++ network_information_statics_GetTrustLevel, ++ /* INetworkInformationStatics methods */ ++ network_information_statics_GetConnectionProfiles, ++ network_information_statics_GetInternetConnectionProfile, ++ network_information_statics_GetLanIdentifiers, ++ network_information_statics_GetHostNames, ++ network_information_statics_GetProxyConfigurationAsync, ++ network_information_statics_GetSortedEndpointPairs, ++ network_information_statics_eventadd_NetworkStatusChanged, ++ network_information_statics_eventremove_NetworkStatusChanged, ++}; ++ + static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_QueryInterface( + IActivationFactory *iface, REFIID iid, void **object) + { ++ struct windows_networking_connectivity *impl = impl_from_IActivationFactory(iface); + TRACE("iface %p, iid %s, object %p stub!\n", iface, debugstr_guid(iid), object); + + if (IsEqualGUID(iid, &IID_IUnknown) || +@@ -48,6 +281,13 @@ static HRESULT STDMETHODCALLTYPE windows_networking_connectivity_QueryInterface( + return S_OK; + } + ++ if (IsEqualGUID(iid, &IID_INetworkInformationStatics)) ++ { ++ IUnknown_AddRef(iface); ++ *object = &impl->INetworkInformationStatics_iface; ++ return S_OK; ++ } ++ + FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *object = NULL; + return E_NOINTERFACE; +@@ -115,6 +355,8 @@ static const struct IActivationFactoryVtbl activation_factory_vtbl = + static struct windows_networking_connectivity windows_networking_connectivity = + { + {&activation_factory_vtbl}, ++ {&network_information_statics_vtbl}, ++ {&vector_view_vtbl}, + 1 + }; + +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch new file mode 100644 index 000000000..89236af3e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0006-windows.networking.connectivity-Registry-DLL.patch @@ -0,0 +1,79 @@ +From 911464a8aba1d14e5191daf47501e39f78b3c9a1 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Mon, 31 May 2021 09:45:56 +1000 +Subject: [PATCH 6/8] windows.networking.connectivity: Registry DLL + +This is a temp solution until the IDL can used for registration. +--- + .../Makefile.in | 3 +++ + .../network.rgs | 19 ++++++++++++++++++ + dlls/windows.networking.connectivity/rsrc.rc | 20 +++++++++++++++++++ + 3 files changed, 42 insertions(+) + create mode 100644 dlls/windows.networking.connectivity/network.rgs + create mode 100644 dlls/windows.networking.connectivity/rsrc.rc + +diff --git a/dlls/windows.networking.connectivity/Makefile.in b/dlls/windows.networking.connectivity/Makefile.in +index 6fc24a72feb..5785430da2c 100644 +--- a/dlls/windows.networking.connectivity/Makefile.in ++++ b/dlls/windows.networking.connectivity/Makefile.in +@@ -5,3 +5,6 @@ EXTRADLLFLAGS = -mno-cygwin + + C_SRCS = \ + windows.networking.connectivity_main.c ++ ++RC_SRCS = rsrc.rc ++ +diff --git a/dlls/windows.networking.connectivity/network.rgs b/dlls/windows.networking.connectivity/network.rgs +new file mode 100644 +index 00000000000..59d6f739a72 +--- /dev/null ++++ b/dlls/windows.networking.connectivity/network.rgs +@@ -0,0 +1,19 @@ ++HKLM ++{ ++ NoRemove Software ++ { ++ NoRemove Microsoft ++ { ++ NoRemove WindowsRuntime ++ { ++ NoRemove ActivatableClassId ++ { ++ ForceRemove Windows.Networking.Connectivity.NetworkInformation ++ { ++ val 'DllPath' = s '%MODULE%' ++ } ++ } ++ } ++ } ++ } ++} +diff --git a/dlls/windows.networking.connectivity/rsrc.rc b/dlls/windows.networking.connectivity/rsrc.rc +new file mode 100644 +index 00000000000..3ebeb3a7000 +--- /dev/null ++++ b/dlls/windows.networking.connectivity/rsrc.rc +@@ -0,0 +1,20 @@ ++/* ++ * Copyright 2021 Alistair Leslie-Hughes ++ * ++ * 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 ++ */ ++ ++/* @makedep: network.rgs */ ++1 WINE_REGISTRY network.rgs +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch new file mode 100644 index 000000000..c3454050d --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0007-windows.networking.connectivity-Implement-INetworkIn.patch @@ -0,0 +1,198 @@ +From ef41fa79b32aa3d211e1e8b64b9ace89f8fa5876 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 1 Jun 2021 13:13:57 +1000 +Subject: [PATCH 7/8] windows.networking.connectivity: Implement + INetworkInformationStatics GetInternetConnectionProfile + +--- + .../windows.networking.connectivity_main.c | 163 +++++++++++++++++- + 1 file changed, 161 insertions(+), 2 deletions(-) + +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +index b96e6c4f0a4..ba1f5a5401d 100644 +--- a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +@@ -5,6 +5,7 @@ + #include "winbase.h" + #include "winstring.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + #include "objbase.h" + #include "initguid.h" +@@ -201,12 +202,170 @@ static HRESULT STDMETHODCALLTYPE network_information_statics_GetConnectionProfil + return S_OK; + } + +-static HRESULT STDMETHODCALLTYPE network_information_statics_GetInternetConnectionProfile(INetworkInformationStatics *iface, IConnectionProfile **value) ++struct connection_profile + { +- FIXME("iface %p, %p stub!\n", iface, value); ++ IConnectionProfile IConnectionProfile_iface; ++ LONG ref; ++}; ++ ++static inline struct connection_profile *impl_from_IConnectionProfile(IConnectionProfile *iface) ++{ ++ return CONTAINING_RECORD(iface, struct connection_profile, IConnectionProfile_iface); ++} ++ ++static HRESULT WINAPI connection_profile_QueryInterface(IConnectionProfile *iface, REFIID riid, void **object) ++{ ++ TRACE("iface %p, iid %s, object %p stub!\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) || ++ IsEqualGUID(riid, &IID_IInspectable) || ++ IsEqualGUID(riid, &IID_IConnectionProfile)) ++ { ++ IUnknown_AddRef(iface); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI connection_profile_AddRef(IConnectionProfile *iface) ++{ ++ struct connection_profile *impl = impl_from_IConnectionProfile(iface); ++ ULONG rc = InterlockedIncrement(&impl->ref); ++ TRACE("%p increasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static ULONG WINAPI connection_profile_Release(IConnectionProfile *iface) ++{ ++ struct connection_profile *impl = impl_from_IConnectionProfile(iface); ++ ULONG rc = InterlockedIncrement(&impl->ref); ++ TRACE("%p increasing refcount to %u.\n", impl, rc); ++ return rc; ++} ++ ++static HRESULT WINAPI connection_profile_GetIids(IConnectionProfile *iface, ULONG *iid_count, IID **iids) ++{ ++ FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetRuntimeClassName(IConnectionProfile *iface, HSTRING *class_name) ++{ ++ FIXME("iface %p, class_name %p stub!\n", iface, class_name); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetTrustLevel(IConnectionProfile *iface, TrustLevel *trust_level) ++{ ++ FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_get_ProfileName(IConnectionProfile *iface, HSTRING *value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); + return E_NOTIMPL; + } + ++static HRESULT WINAPI connection_profile_GetNetworkConnectivityLevel(IConnectionProfile *iface, ++ enum __x_ABI_CWindows_CNetworking_CConnectivity_CNetworkConnectivityLevel *value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetNetworkNames(IConnectionProfile *iface, __FIVectorView_1_HSTRING **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetConnectionCost(IConnectionProfile *iface, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CIConnectionCost **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetDataPlanStatus(IConnectionProfile *iface, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CIDataPlanStatus **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_get_NetworkAdapter(IConnectionProfile *iface, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CINetworkAdapter **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetLocalUsage(IConnectionProfile *iface, ++ struct __x_ABI_CWindows_CFoundation_CDateTime start, struct __x_ABI_CWindows_CFoundation_CDateTime end, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CIDataUsage **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_GetLocalUsagePerRoamingStates(IConnectionProfile *iface, ++ struct __x_ABI_CWindows_CFoundation_CDateTime start, struct __x_ABI_CWindows_CFoundation_CDateTime end, ++ enum __x_ABI_CWindows_CNetworking_CConnectivity_CRoamingStates states, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CIDataUsage **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI connection_profile_get_NetworkSecuritySettings(IConnectionProfile *iface, ++ __x_ABI_CWindows_CNetworking_CConnectivity_CINetworkSecuritySettings **value) ++{ ++ FIXME("iface %p, value %p stub!\n", iface, value); ++ return E_NOTIMPL; ++} ++ ++struct __x_ABI_CWindows_CNetworking_CConnectivity_CIConnectionProfileVtbl connection_vtbl = ++{ ++ connection_profile_QueryInterface, ++ connection_profile_AddRef, ++ connection_profile_Release, ++ connection_profile_GetIids, ++ connection_profile_GetRuntimeClassName, ++ connection_profile_GetTrustLevel, ++ connection_profile_get_ProfileName, ++ connection_profile_GetNetworkConnectivityLevel, ++ connection_profile_GetNetworkNames, ++ connection_profile_GetConnectionCost, ++ connection_profile_GetDataPlanStatus, ++ connection_profile_get_NetworkAdapter, ++ connection_profile_GetLocalUsage, ++ connection_profile_GetLocalUsagePerRoamingStates, ++ connection_profile_get_NetworkSecuritySettings ++}; ++ ++static HRESULT STDMETHODCALLTYPE network_information_statics_GetInternetConnectionProfile(INetworkInformationStatics *iface, IConnectionProfile **value) ++{ ++ struct windows_networking_connectivity *impl = impl_from_INetworkInformationStatics(iface); ++ struct connection_profile *profile; ++ ++ FIXME("iface %p, %p stub!\n", impl, value); ++ ++ profile = heap_alloc(sizeof(struct connection_profile)); ++ if (!profile) ++ return E_OUTOFMEMORY; ++ ++ profile->IConnectionProfile_iface.lpVtbl = &connection_vtbl; ++ profile->ref = 1; ++ ++ *value = &profile->IConnectionProfile_iface; ++ return S_OK; ++} ++ + static HRESULT STDMETHODCALLTYPE network_information_statics_GetLanIdentifiers(INetworkInformationStatics *iface, __FIVectorView_1_Windows__CNetworking__CConnectivity__CLanIdentifier **value) + { + FIXME("iface %p, %p stub!\n", iface, value); +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch new file mode 100644 index 000000000..a72b32cce --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/0008-windows.networking.connectivity-IConnectionProfile-G.patch @@ -0,0 +1,30 @@ +From 308cffe2fdf351c360870cd96c04b3384a70bd7a Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Tue, 1 Jun 2021 13:24:16 +1000 +Subject: [PATCH 8/8] windows.networking.connectivity: IConnectionProfile + GetNetworkConnectivityLevel always return internet access + +--- + .../windows.networking.connectivity_main.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +index ba1f5a5401d..0e8f4e699f0 100644 +--- a/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c ++++ b/dlls/windows.networking.connectivity/windows.networking.connectivity_main.c +@@ -274,8 +274,10 @@ static HRESULT WINAPI connection_profile_get_ProfileName(IConnectionProfile *ifa + static HRESULT WINAPI connection_profile_GetNetworkConnectivityLevel(IConnectionProfile *iface, + enum __x_ABI_CWindows_CNetworking_CConnectivity_CNetworkConnectivityLevel *value) + { +- FIXME("iface %p, value %p stub!\n", iface, value); +- return E_NOTIMPL; ++ struct connection_profile *impl = impl_from_IConnectionProfile(iface); ++ FIXME("iface %p, value %p stub!\n", impl, value); ++ *value = NetworkConnectivityLevel_InternetAccess; ++ return S_OK; + } + + static HRESULT WINAPI connection_profile_GetNetworkNames(IConnectionProfile *iface, __FIVectorView_1_HSTRING **value) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/definition new file mode 100644 index 000000000..c3764f192 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/windows.networking.connectivity-new-dll/definition @@ -0,0 +1 @@ +Fixes: [46534] Implement INetworkInformationStatics interface diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/0001-wineboot-Initialize-proxy-settings-registry-key.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/0001-wineboot-Initialize-proxy-settings-registry-key.patch new file mode 100644 index 000000000..cc057202e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/0001-wineboot-Initialize-proxy-settings-registry-key.patch @@ -0,0 +1,59 @@ +From 584eecc73a8cba537b8f50932769729a29fe7d3d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Mon, 26 Dec 2016 16:37:40 +0100 +Subject: [PATCH] wineboot: Initialize proxy settings registry key. + +--- + programs/wineboot/Makefile.in | 2 +- + programs/wineboot/wineboot.c | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/programs/wineboot/Makefile.in b/programs/wineboot/Makefile.in +index 667f8f48702..4a1747ad047 100644 +--- a/programs/wineboot/Makefile.in ++++ b/programs/wineboot/Makefile.in +@@ -1,6 +1,6 @@ + MODULE = wineboot.exe + IMPORTS = uuid advapi32 ws2_32 kernelbase +-DELAYIMPORTS = shell32 shlwapi version user32 setupapi newdev crypt32 ++DELAYIMPORTS = shell32 shlwapi version user32 setupapi newdev crypt32 wininet + + EXTRADLLFLAGS = -mconsole + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 14943b005ef..9aab3b68e5b 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -77,6 +77,7 @@ + #include + #include + #include ++#include + #include + #include "resource.h" + +@@ -1002,6 +1003,13 @@ static void create_volatile_environment_registry_key(void) + RegCloseKey( hkey ); + } + ++static void create_proxy_settings(void) ++{ ++ HINTERNET inet; ++ inet = InternetOpenA( "Wine", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); ++ if (inet) InternetCloseHandle( inet ); ++} ++ + /* Performs the rename operations dictated in %SystemRoot%\Wininit.ini. + * Returns FALSE if there was an error, or otherwise if all is ok. + */ +@@ -1812,6 +1820,7 @@ int __cdecl main( int argc, char *argv[] ) + if (init || update) update_wineprefix( update ); + + create_volatile_environment_registry_key(); ++ create_proxy_settings(); + + ProcessRunKeys( HKEY_LOCAL_MACHINE, L"RunOnce", TRUE, TRUE ); + +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/definition new file mode 100644 index 000000000..9e38c54cb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wineboot-ProxySettings/definition @@ -0,0 +1 @@ +Fixes: [42024] Create ProxyEnable key on wineprefix update diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f-be.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f-be.patch new file mode 100644 index 000000000..6942a0a59 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f-be.patch @@ -0,0 +1,94 @@ +From b6c918acf5996c494bece9efb1439fe00b81a274 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Fri, 1 Jun 2018 14:03:26 +1000 +Subject: [PATCH] winex11: Specify a default vulkan driver if one not found at + build time + +We cannot specify it as a dependency since Debian Jessie has the +vulkan library in backports and not everybody will have this mapped. +--- + dlls/winex11.drv/vulkan.c | 38 ++++++++++++++++---------------------- + 1 file changed, 16 insertions(+), 22 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index f4fe202dfaf..1768444b2e5 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -40,10 +40,12 @@ + #include "wine/vulkan_driver.h" + + WINE_DEFAULT_DEBUG_CHANNEL(vulkan); +- +-#ifdef SONAME_LIBVULKAN + WINE_DECLARE_DEBUG_CHANNEL(fps); + ++#ifndef SONAME_LIBVULKAN ++#define SONAME_LIBVULKAN "" ++#endif ++ + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; +@@ -104,14 +106,23 @@ static void *vulkan_handle; + + static void wine_vk_init(void) + { +- init_recursive_mutex(&vulkan_mutex); ++ const char *libvulkan_candidates[] = {SONAME_LIBVULKAN, ++ "libvulkan.so.1", ++ "libvulkan.so", ++ NULL}; ++ int i; + +- if (!(vulkan_handle = dlopen(SONAME_LIBVULKAN, RTLD_NOW))) ++ for (i=0; libvulkan_candidates[i] && !vulkan_handle; i++) ++ vulkan_handle = dlopen(libvulkan_candidates[i], RTLD_NOW); ++ ++ if (!vulkan_handle) + { +- ERR("Failed to load %s.\n", SONAME_LIBVULKAN); ++ ERR("Failed to load vulkan library\n"); + return; + } + ++ init_recursive_mutex(&vulkan_mutex); ++ + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) + LOAD_FUNCPTR(vkCreateInstance); +@@ -1270,32 +1270,3 @@ const struct vulkan_funcs *get_vulkan_driver(UINT version) + return NULL; + } + +-#else /* No vulkan */ +- +-const struct vulkan_funcs *get_vulkan_driver(UINT version) +-{ +- ERR("Wine was built without Vulkan support.\n"); +- return NULL; +-} +- +-void destroy_vk_surface(HWND hwnd) +-{ +-} +- +-void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) +-{ +-} +- +-void sync_vk_surface(HWND hwnd, BOOL known_child) +-{ +-} +- +-void invalidate_vk_surfaces(HWND hwnd) +-{ +-} +- +-void vulkan_thread_detach(void) +-{ +-} +- +-#endif /* SONAME_LIBVULKAN */ +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch new file mode 100644 index 000000000..8d0149a96 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/0001-winex11-Specify-a-default-vulkan-driver-if-one-not-f.patch @@ -0,0 +1,94 @@ +From b6c918acf5996c494bece9efb1439fe00b81a274 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Fri, 1 Jun 2018 14:03:26 +1000 +Subject: [PATCH] winex11: Specify a default vulkan driver if one not found at + build time + +We cannot specify it as a dependency since Debian Jessie has the +vulkan library in backports and not everybody will have this mapped. +--- + dlls/winex11.drv/vulkan.c | 38 ++++++++++++++++---------------------- + 1 file changed, 16 insertions(+), 22 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index f4fe202dfaf..1768444b2e5 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -40,10 +40,12 @@ + #include "wine/vulkan_driver.h" + + WINE_DEFAULT_DEBUG_CHANNEL(vulkan); +- +-#ifdef SONAME_LIBVULKAN + WINE_DECLARE_DEBUG_CHANNEL(fps); + ++#ifndef SONAME_LIBVULKAN ++#define SONAME_LIBVULKAN "" ++#endif ++ + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; +@@ -104,14 +106,23 @@ static void *vulkan_handle; + + static void wine_vk_init(void) + { +- init_recursive_mutex(&vulkan_mutex); ++ const char *libvulkan_candidates[] = {SONAME_LIBVULKAN, ++ "libvulkan.so.1", ++ "libvulkan.so", ++ NULL}; ++ int i; + +- if (!(vulkan_handle = dlopen(SONAME_LIBVULKAN, RTLD_NOW))) ++ for (i=0; libvulkan_candidates[i] && !vulkan_handle; i++) ++ vulkan_handle = dlopen(libvulkan_candidates[i], RTLD_NOW); ++ ++ if (!vulkan_handle) + { +- ERR("Failed to load %s.\n", SONAME_LIBVULKAN); ++ ERR("Failed to load vulkan library\n"); + return; + } + ++ init_recursive_mutex(&vulkan_mutex); ++ + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) + LOAD_FUNCPTR(vkCreateInstance); +@@ -1270,32 +1270,3 @@ const struct vulkan_funcs *get_vulkan_driver(UINT version) + return NULL; + } + +-#else /* No vulkan */ +- +-const struct vulkan_funcs *get_vulkan_driver(UINT version) +-{ +- ERR("Wine was built without Vulkan support.\n"); +- return NULL; +-} +- +-void destroy_vk_surface(HWND hwnd) +-{ +-} +- +-void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges changes) +-{ +-} +- +-void sync_vk_surface(HWND hwnd, BOOL known_child) +-{ +-} +- +-void invalidate_vk_surfaces(HWND hwnd) +-{ +-} +- +-void vulkan_thread_detach(void) +-{ +-} +- +-#endif /* SONAME_LIBVULKAN */ +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/definition new file mode 100644 index 000000000..e99592c9b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/winex11-Vulkan_support/definition @@ -0,0 +1 @@ +Fixes: [44775] Allow vulkan support to be detected at runtime. diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0001-wininet-tests-Add-more-tests-for-cookies.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0001-wininet-tests-Add-more-tests-for-cookies.patch new file mode 100644 index 000000000..a57826339 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0001-wininet-tests-Add-more-tests-for-cookies.patch @@ -0,0 +1,145 @@ +From b157667185244a4e963fa3c161c76ea53410d16d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 15 May 2015 20:37:19 +0200 +Subject: [PATCH] wininet/tests: Add more tests for cookies. + +--- + dlls/wininet/tests/http.c | 92 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 89 insertions(+), 3 deletions(-) + +diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c +index 98d1b74ca85..308d6a74c20 100644 +--- a/dlls/wininet/tests/http.c ++++ b/dlls/wininet/tests/http.c +@@ -2105,6 +2105,14 @@ static const char largemsg[] = + "Content-Length: %I64u\r\n" + "\r\n"; + ++static const char okmsg_cookie_path[] = ++"HTTP/1.1 200 OK\r\n" ++"Date: Mon, 01 Dec 2008 13:44:34 GMT\r\n" ++"Server: winetest\r\n" ++"Content-Length: 0\r\n" ++"Set-Cookie: subcookie2=data; path=/test_cookie_set_path\r\n" ++"\r\n"; ++ + static const char notokmsg[] = + "HTTP/1.1 400 Bad Request\r\n" + "Server: winetest\r\n" +@@ -2524,6 +2532,32 @@ static DWORD CALLBACK server_thread(LPVOID param) + else + send(c, noauthmsg, sizeof noauthmsg-1, 0); + } ++ if (strstr(buffer, "/test_cookie_path1")) ++ { ++ if (strstr(buffer, "subcookie=data")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, notokmsg, sizeof notokmsg-1, 0); ++ } ++ if (strstr(buffer, "/test_cookie_path2")) ++ { ++ if (strstr(buffer, "subcookie2=data")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, notokmsg, sizeof notokmsg-1, 0); ++ } ++ if (strstr(buffer, "/test_cookie_set_path")) ++ { ++ send(c, okmsg_cookie_path, sizeof okmsg_cookie_path-1, 0); ++ } ++ if (strstr(buffer, "/test_cookie_merge")) ++ { ++ if (strstr(buffer, "subcookie=data") && ++ !strstr(buffer, "manual_cookie=test")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, notokmsg, sizeof notokmsg-1, 0); ++ } + if (strstr(buffer, "/test_host_override")) + { + if (strstr(buffer, host_header_override)) +@@ -4037,7 +4071,7 @@ static void test_cookie_header(int port) + HINTERNET ses, con, req; + DWORD size, error; + BOOL ret; +- char buffer[64]; ++ char buffer[256]; + + ses = InternetOpenA("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ok(ses != NULL, "InternetOpen failed\n"); +@@ -4065,7 +4099,7 @@ static void test_cookie_header(int port) + size = sizeof(buffer); + ret = HttpQueryInfoA(req, HTTP_QUERY_COOKIE | HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer, &size, NULL); + ok(ret, "HttpQueryInfo failed: %lu\n", GetLastError()); +- ok(!strcmp(buffer, "cookie=not biscuit"), "got '%s' expected \'cookie=not biscuit\'\n", buffer); ++ ok(!!strstr(buffer, "cookie=not biscuit"), "got '%s' expected \'cookie=not biscuit\'\n", buffer); + + ret = HttpSendRequestA(req, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed: %lu\n", GetLastError()); +@@ -4076,9 +4110,61 @@ static void test_cookie_header(int port) + size = sizeof(buffer); + ret = HttpQueryInfoA(req, HTTP_QUERY_COOKIE | HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer, &size, NULL); + ok(ret, "HttpQueryInfo failed: %lu\n", GetLastError()); +- ok(!strcmp(buffer, "cookie=biscuit"), "got '%s' expected \'cookie=biscuit\'\n", buffer); ++ ok(!strstr(buffer, "cookie=not biscuit"), "'%s' should not contain \'cookie=not biscuit\'\n", buffer); ++ ok(!!strstr(buffer, "cookie=biscuit"), "'%s' should contain \'cookie=biscuit\'\n", buffer); + + InternetCloseHandle(req); ++ ++ InternetSetCookieA("http://localhost/testCCCC", "subcookie", "data"); ++ ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_path1", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ InternetCloseHandle(req); ++ ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_path1/abc", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ InternetCloseHandle(req); ++ ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_set_path", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ InternetCloseHandle(req); ++ ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_path2", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 400); ++ InternetCloseHandle(req); ++ ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_merge", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpAddRequestHeadersA(req, "Cookie: manual_cookie=test\r\n", ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed: %lu\n", GetLastError()); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ InternetCloseHandle(req); ++ + InternetCloseHandle(con); + InternetCloseHandle(ses); + } +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0002-wininet-tests-Test-auth-credential-reusage-with-host.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0002-wininet-tests-Test-auth-credential-reusage-with-host.patch new file mode 100644 index 000000000..9b7d9d378 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0002-wininet-tests-Test-auth-credential-reusage-with-host.patch @@ -0,0 +1,130 @@ +From 303a7d54eca11f350f200bf3747646349a84536f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 15 May 2015 21:18:37 +0200 +Subject: [PATCH] wininet/tests: Test auth credential reusage with host + override. + +--- + dlls/wininet/tests/http.c | 93 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 93 insertions(+) + +diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c +index b06bd6c04d1..59689baf87e 100644 +--- a/dlls/wininet/tests/http.c ++++ b/dlls/wininet/tests/http.c +@@ -2496,12 +2496,27 @@ static DWORD CALLBACK server_thread(LPVOID param) + { + send(c, okmsg, sizeof(okmsg)-1, 0); + } ++ + if (strstr(buffer, "HEAD /test_large_content")) + { + char msg[sizeof(largemsg) + 16]; + sprintf(msg, largemsg, content_length); + send(c, msg, strlen(msg), 0); + } ++ if (strstr(buffer, "HEAD /test_auth_host1")) ++ { ++ if (strstr(buffer, "Authorization: Basic dGVzdDE6cGFzcw==")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, noauthmsg, sizeof noauthmsg-1, 0); ++ } ++ if (strstr(buffer, "HEAD /test_auth_host2")) ++ { ++ if (strstr(buffer, "Authorization: Basic dGVzdDE6cGFzczI=")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, noauthmsg, sizeof noauthmsg-1, 0); ++ } + shutdown(c, 2); + closesocket(c); + c = -1; +@@ -3200,6 +3215,84 @@ static void test_header_override(int port) + InternetCloseHandle(req); + InternetCloseHandle(con); + InternetCloseHandle(ses); ++ ++ ses = InternetOpenA("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); ++ ok(ses != NULL, "InternetOpenA failed\n"); ++ ++ con = InternetConnectA(ses, "localhost", port, "test1", "pass", INTERNET_SERVICE_HTTP, 0, 0); ++ ok(con != NULL, "InternetConnectA failed %lu\n", GetLastError()); ++ ++ req = HttpOpenRequestA( con, "HEAD", "/test_auth_host1", NULL, NULL, NULL, 0, 0); ++ ok(req != NULL, "HttpOpenRequestA failed %lu\n", GetLastError()); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequestA failed %lu\n", GetLastError()); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ InternetCloseHandle(con); ++ InternetCloseHandle(ses); ++ ++ ses = InternetOpenA("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); ++ ok(ses != NULL, "InternetOpenA failed\n"); ++ ++ con = InternetConnectA( ses, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); ++ ok(con != NULL, "InternetConnectA failed %lu\n", GetLastError()); ++ ++ req = HttpOpenRequestA(con, "HEAD", "/test_auth_host1", NULL, NULL, NULL, 0, 0); ++ ok(req != NULL, "HttpOpenRequestA failed %lu\n", GetLastError()); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA( req, NULL, 0, NULL, 0 ); ++ ok( ret, "HttpSendRequestA failed %lu\n", GetLastError() ); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ InternetCloseHandle(con); ++ InternetCloseHandle(ses); ++ ++ ses = InternetOpenA("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); ++ ok(ses != NULL, "InternetOpenA failed\n"); ++ ++ con = InternetConnectA(ses, "localhost", port, "test1", "pass2", INTERNET_SERVICE_HTTP, 0, 0); ++ ok(con != NULL, "InternetConnectA failed %lu\n", GetLastError()); ++ ++ req = HttpOpenRequestA(con, "HEAD", "/test_auth_host2", NULL, NULL, NULL, 0, 0); ++ ok(req != NULL, "HttpOpenRequestA failed %lu\n", GetLastError()); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequestA failed %lu\n", GetLastError()); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ InternetCloseHandle(con); ++ InternetCloseHandle(ses); ++ ++ ses = InternetOpenA("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); ++ ok(ses != NULL, "InternetOpenA failed\n"); ++ ++ con = InternetConnectA(ses, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); ++ ok(con != NULL, "InternetConnectA failed %lu\n", GetLastError()); ++ ++ req = HttpOpenRequestA(con, "HEAD", "/test_auth_host2", NULL, NULL, NULL, 0, 0); ++ ok(req != NULL, "HttpOpenRequestA failed %lu\n", GetLastError()); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequestA failed %lu\n", GetLastError()); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ InternetCloseHandle(con); ++ InternetCloseHandle(ses); + } + + static void test_connection_closing(int port) +-- +2.17.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0003-wininet-tests-Check-cookie-behaviour-when-overriding.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0003-wininet-tests-Check-cookie-behaviour-when-overriding.patch new file mode 100644 index 000000000..87b82484a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0003-wininet-tests-Check-cookie-behaviour-when-overriding.patch @@ -0,0 +1,132 @@ +From 691cc2563d203b7b8862406fbd373f61bccea9e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Fri, 15 May 2015 23:09:20 +0200 +Subject: [PATCH] wininet/tests: Check cookie behaviour when overriding host. + +--- + dlls/wininet/tests/http.c | 95 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 95 insertions(+) + +diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c +index 43eba2db172..2ccba48fa2f 100644 +--- a/dlls/wininet/tests/http.c ++++ b/dlls/wininet/tests/http.c +@@ -2113,6 +2113,14 @@ static const char okmsg_cookie_path[] = + "Set-Cookie: subcookie2=data; path=/test_cookie_set_path\r\n" + "\r\n"; + ++static const char okmsg_cookie[] = ++"HTTP/1.1 200 OK\r\n" ++"Date: Mon, 01 Dec 2008 13:44:34 GMT\r\n" ++"Server: winetest\r\n" ++"Content-Length: 0\r\n" ++"Set-Cookie: testcookie=testvalue\r\n" ++"\r\n"; ++ + static const char notokmsg[] = + "HTTP/1.1 400 Bad Request\r\n" + "Server: winetest\r\n" +@@ -2558,6 +2566,25 @@ static DWORD CALLBACK server_thread(LPVOID param) + else + send(c, notokmsg, sizeof notokmsg-1, 0); + } ++ if (strstr(buffer, "/test_cookie_set_host_override")) ++ { ++ send(c, okmsg_cookie, sizeof okmsg_cookie-1, 0); ++ } ++ if (strstr(buffer, "/test_cookie_check_host_override")) ++ { ++ if (strstr(buffer, "Cookie:") && strstr(buffer, "testcookie=testvalue")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, notokmsg, sizeof notokmsg-1, 0); ++ } ++ if (strstr(buffer, "/test_cookie_check_different_host")) ++ { ++ if (!strstr(buffer, "foo") && ++ strstr(buffer, "cookie=biscuit")) ++ send(c, okmsg, sizeof okmsg-1, 0); ++ else ++ send(c, notokmsg, sizeof notokmsg-1, 0); ++ } + if (strstr(buffer, "/test_host_override")) + { + if (strstr(buffer, host_header_override)) +@@ -3305,6 +3332,74 @@ static void test_header_override(int port) + test_status_code(req, 400); + } + ++ InternetCloseHandle(req); ++ InternetSetCookieA("http://localhost", "cookie", "biscuit"); ++ req = HttpOpenRequestA(con, NULL, "/testC", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_set_host_override", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_check_host_override", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code_todo(req, 200); ++ ++ InternetCloseHandle(req); ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_check_host_override", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code_todo(req, 200); ++ ++ InternetCloseHandle(req); ++ InternetSetCookieA("http://test.local", "foo", "bar"); ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_check_different_host", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ ++ InternetCloseHandle(req); ++ req = HttpOpenRequestA(con, NULL, "/test_cookie_check_different_host", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); ++ ok(req != NULL, "HttpOpenRequest failed\n"); ++ ++ ret = HttpAddRequestHeadersA(req, host_header_override, ~0u, HTTP_ADDREQ_FLAG_ADD); ++ ok(ret, "HttpAddRequestHeaders failed\n"); ++ ++ ret = HttpSendRequestA(req, NULL, 0, NULL, 0); ++ ok(ret, "HttpSendRequest failed\n"); ++ ++ test_status_code(req, 200); ++ + InternetCloseHandle(req); + InternetCloseHandle(con); + InternetCloseHandle(ses); +-- +2.35.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0004-wininet-Strip-filename-if-no-path-is-set-in-cookie.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0004-wininet-Strip-filename-if-no-path-is-set-in-cookie.patch new file mode 100644 index 000000000..11671c83e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0004-wininet-Strip-filename-if-no-path-is-set-in-cookie.patch @@ -0,0 +1,82 @@ +From 20e7de7c8f73fb27f7eeffbc5de646fc27fe1bb7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Sat, 16 May 2015 00:24:35 +0200 +Subject: [PATCH] wininet: Strip filename if no path is set in cookie. + +The order of the stored cookies doesn't match in /testC, so +be a bit less strict in the test. +--- + dlls/wininet/http.c | 11 ++++++++++- + dlls/wininet/tests/http.c | 6 +++--- + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c +index c770c312ba9..d95b39bd9ce 100644 +--- a/dlls/wininet/http.c ++++ b/dlls/wininet/http.c +@@ -654,10 +654,18 @@ static void HTTP_ProcessCookies( http_request_t *request ) + int HeaderIndex; + int numCookies = 0; + LPHTTPHEADERW setCookieHeader; ++ WCHAR *path, *tmp; + + if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) + return; + ++ path = wcsdup(request->path); ++ if (!path) ++ return; ++ ++ tmp = wcsrchr(path, '/'); ++ if (tmp && tmp[1]) tmp[1] = 0; ++ + EnterCriticalSection( &request->headers_section ); + + while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, L"Set-Cookie", numCookies++, FALSE)) != -1) +@@ -676,10 +684,11 @@ static void HTTP_ProcessCookies( http_request_t *request ) + + name = substr(setCookieHeader->lpszValue, data - setCookieHeader->lpszValue); + data++; +- set_cookie(substrz(request->server->name), substrz(request->path), name, substrz(data), INTERNET_COOKIE_HTTPONLY); ++ set_cookie(substrz(request->server->name), substrz(path), name, substrz(data), INTERNET_COOKIE_HTTPONLY); + } + + LeaveCriticalSection( &request->headers_section ); ++ free(path); + } + + static void strip_spaces(LPWSTR start) +diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c +index 510c3ac5232..b98e648c276 100644 +--- a/dlls/wininet/tests/http.c ++++ b/dlls/wininet/tests/http.c +@@ -2363,7 +2363,7 @@ static DWORD CALLBACK server_thread(LPVOID param) + } + if (strstr(buffer, "/testC")) + { +- if (strstr(buffer, "Cookie: cookie=biscuit")) ++ if (strstr(buffer, "cookie=biscuit")) + send(c, okmsg, sizeof okmsg-1, 0); + else + send(c, notokmsg, sizeof notokmsg-1, 0); +@@ -3351,7 +3351,7 @@ static void test_header_override(int port) + ret = HttpSendRequestA(req, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed\n"); + +- test_status_code_todo(req, 200); ++ test_status_code(req, 200); + + InternetCloseHandle(req); + req = HttpOpenRequestA(con, NULL, "/test_cookie_check_host_override", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0); +@@ -3360,7 +3360,7 @@ static void test_header_override(int port) + ret = HttpSendRequestA(req, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed\n"); + +- test_status_code_todo(req, 200); ++ test_status_code(req, 200); + + InternetCloseHandle(req); + InternetSetCookieA("http://test.local", "foo", "bar"); +-- +2.29.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0005-wininet-Replacing-header-fields-should-fail-if-they-.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0005-wininet-Replacing-header-fields-should-fail-if-they-.patch new file mode 100644 index 000000000..1768f399a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/0005-wininet-Replacing-header-fields-should-fail-if-they-.patch @@ -0,0 +1,250 @@ +From 10bdb80d18fa95c3bffd61217015e1512b14e0d7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Sat, 16 May 2015 03:16:15 +0200 +Subject: [PATCH] wininet: Replacing header fields should fail if they do not + exist yet. + +A lot of details are not properly covered by tests yet and were +marked with FIXME comments. The implementation was written in such +a way that it behaves identical to the old code in such situations. +--- + dlls/wininet/http.c | 197 ++++++++++++++++++++++---------------------- + 1 file changed, 99 insertions(+), 98 deletions(-) + +diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c +index 18be0683d3a..645e7825a5a 100644 +--- a/dlls/wininet/http.c ++++ b/dlls/wininet/http.c +@@ -6113,130 +6113,131 @@ static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer) + + static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier) + { +- LPHTTPHEADERW lphttpHdr = NULL; ++ LPHTTPHEADERW lphttpHdr; + INT index; + BOOL request_only = !!(dwModifier & HTTP_ADDHDR_FLAG_REQ); +- DWORD res = ERROR_HTTP_INVALID_HEADER; ++ DWORD res = ERROR_SUCCESS; + + TRACE("--> %s: %s - 0x%08lx\n", debugstr_w(field), debugstr_w(value), dwModifier); + + EnterCriticalSection( &request->headers_section ); + +- /* REPLACE wins out over ADD */ +- if (dwModifier & HTTP_ADDREQ_FLAG_REPLACE) +- dwModifier &= ~HTTP_ADDREQ_FLAG_ADD; ++ index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only); ++ if (index >= 0) ++ { ++ lphttpHdr = &request->custHeaders[index]; + +- if (dwModifier & HTTP_ADDREQ_FLAG_ADD) +- index = -1; +- else +- index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only); +- +- if (index >= 0) +- { +- if (dwModifier & HTTP_ADDREQ_FLAG_ADD_IF_NEW) ++ /* replace existing header if FLAG_REPLACE is given */ ++ if (dwModifier & HTTP_ADDREQ_FLAG_REPLACE) + { +- LeaveCriticalSection( &request->headers_section ); +- return ERROR_HTTP_INVALID_HEADER; +- } +- lphttpHdr = &request->custHeaders[index]; +- } +- else if (value) +- { +- HTTPHEADERW hdr; ++ HTTP_DeleteCustomHeader( request, index ); + +- hdr.lpszField = (LPWSTR)field; +- hdr.lpszValue = (LPWSTR)value; +- hdr.wFlags = hdr.wCount = 0; ++ if (value && value[0]) ++ { ++ HTTPHEADERW hdr; + +- if (dwModifier & HTTP_ADDHDR_FLAG_REQ) +- hdr.wFlags |= HDR_ISREQUEST; ++ hdr.lpszField = (LPWSTR)field; ++ hdr.lpszValue = (LPWSTR)value; ++ hdr.wFlags = hdr.wCount = 0; + +- res = HTTP_InsertCustomHeader(request, &hdr); +- LeaveCriticalSection( &request->headers_section ); +- return res; +- } +- /* no value to delete */ +- else +- { +- LeaveCriticalSection( &request->headers_section ); +- return ERROR_SUCCESS; +- } ++ if (dwModifier & HTTP_ADDHDR_FLAG_REQ) ++ hdr.wFlags |= HDR_ISREQUEST; + +- if (dwModifier & HTTP_ADDHDR_FLAG_REQ) +- lphttpHdr->wFlags |= HDR_ISREQUEST; +- else +- lphttpHdr->wFlags &= ~HDR_ISREQUEST; ++ res = HTTP_InsertCustomHeader( request, &hdr ); ++ } + +- if (dwModifier & HTTP_ADDREQ_FLAG_REPLACE) +- { +- HTTP_DeleteCustomHeader( request, index ); ++ goto out; ++ } ++ ++ /* do not add new header if FLAG_ADD_IF_NEW is set */ ++ if (dwModifier & HTTP_ADDREQ_FLAG_ADD_IF_NEW) ++ { ++ res = ERROR_HTTP_INVALID_HEADER; /* FIXME */ ++ goto out; ++ } + +- if (value && value[0]) ++ /* handle appending to existing header */ ++ if (dwModifier & COALESCEFLAGS) + { +- HTTPHEADERW hdr; ++ LPWSTR lpsztmp; ++ WCHAR ch = 0; ++ INT len = 0; ++ INT origlen = lstrlenW(lphttpHdr->lpszValue); ++ INT valuelen = lstrlenW(value); ++ ++ /* FIXME: Should it really clear HDR_ISREQUEST? */ ++ if (dwModifier & HTTP_ADDHDR_FLAG_REQ) ++ lphttpHdr->wFlags |= HDR_ISREQUEST; ++ else ++ lphttpHdr->wFlags &= ~HDR_ISREQUEST; + +- hdr.lpszField = (LPWSTR)field; +- hdr.lpszValue = (LPWSTR)value; +- hdr.wFlags = hdr.wCount = 0; ++ if (dwModifier & HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ++ { ++ ch = ','; ++ lphttpHdr->wFlags |= HDR_COMMADELIMITED; ++ } ++ else if (dwModifier & HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON) ++ { ++ ch = ';'; ++ lphttpHdr->wFlags |= HDR_COMMADELIMITED; ++ } + +- if (dwModifier & HTTP_ADDHDR_FLAG_REQ) +- hdr.wFlags |= HDR_ISREQUEST; ++ len = origlen + valuelen + ((ch > 0) ? 2 : 0); + +- res = HTTP_InsertCustomHeader(request, &hdr); +- LeaveCriticalSection( &request->headers_section ); +- return res; +- } ++ lpsztmp = realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR)); ++ if (lpsztmp) ++ { ++ lphttpHdr->lpszValue = lpsztmp; ++ /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */ ++ if (ch > 0) ++ { ++ lphttpHdr->lpszValue[origlen] = ch; ++ origlen++; ++ lphttpHdr->lpszValue[origlen] = ' '; ++ origlen++; ++ } + +- LeaveCriticalSection( &request->headers_section ); +- return ERROR_SUCCESS; +- } +- else if (dwModifier & COALESCEFLAGS) ++ memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR)); ++ lphttpHdr->lpszValue[len] = '\0'; ++ } ++ else ++ { ++ WARN("realloc (%d bytes) failed\n",len+1); ++ res = ERROR_OUTOFMEMORY; ++ } ++ ++ goto out; ++ } ++ } ++ ++ /* FIXME: What about other combinations? */ ++ if ((dwModifier & ~HTTP_ADDHDR_FLAG_REQ) == HTTP_ADDREQ_FLAG_REPLACE) + { +- LPWSTR lpsztmp; +- WCHAR ch = 0; +- INT len = 0; +- INT origlen = lstrlenW(lphttpHdr->lpszValue); +- INT valuelen = lstrlenW(value); ++ res = ERROR_HTTP_HEADER_NOT_FOUND; ++ goto out; ++ } + +- if (dwModifier & HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) +- { +- ch = ','; +- lphttpHdr->wFlags |= HDR_COMMADELIMITED; +- } +- else if (dwModifier & HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON) +- { +- ch = ';'; +- lphttpHdr->wFlags |= HDR_COMMADELIMITED; +- } ++ /* FIXME: What if value == ""? */ ++ if (value) ++ { ++ HTTPHEADERW hdr; + +- len = origlen + valuelen + ((ch > 0) ? 2 : 0); ++ hdr.lpszField = (LPWSTR)field; ++ hdr.lpszValue = (LPWSTR)value; ++ hdr.wFlags = hdr.wCount = 0; + +- lpsztmp = realloc(lphttpHdr->lpszValue, (len + 1) * sizeof(WCHAR)); +- if (lpsztmp) +- { +- lphttpHdr->lpszValue = lpsztmp; +- /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */ +- if (ch > 0) +- { +- lphttpHdr->lpszValue[origlen] = ch; +- origlen++; +- lphttpHdr->lpszValue[origlen] = ' '; +- origlen++; +- } ++ if (dwModifier & HTTP_ADDHDR_FLAG_REQ) ++ hdr.wFlags |= HDR_ISREQUEST; + +- memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR)); +- lphttpHdr->lpszValue[len] = '\0'; +- res = ERROR_SUCCESS; +- } +- else +- { +- WARN("realloc (%d bytes) failed\n",len+1); +- res = ERROR_OUTOFMEMORY; +- } ++ res = HTTP_InsertCustomHeader( request, &hdr ); ++ goto out; + } +- TRACE("<-- %ld\n", res); +- LeaveCriticalSection( &request->headers_section ); +- return res; ++ ++ /* FIXME: What if value == NULL? */ ++out: ++ TRACE("<-- %ld\n", res); ++ LeaveCriticalSection( &request->headers_section ); ++ return res; + } + + /*********************************************************************** +-- +2.38.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/definition new file mode 100644 index 000000000..52d34a965 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wininet-Cleanup/definition @@ -0,0 +1 @@ +# Fixes: [28911] Add HTTP Host header in HttpSendRequest instead of HttpOpenRequest diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/0001-wintrust-Add-parameter-check-in-WTHelperGetProvCertF.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/0001-wintrust-Add-parameter-check-in-WTHelperGetProvCertF.patch new file mode 100644 index 000000000..daf82ec36 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/0001-wintrust-Add-parameter-check-in-WTHelperGetProvCertF.patch @@ -0,0 +1,26 @@ +From 563f0ccc4f47914e1e2952cc4bc5673cbb97a5ae Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Wed, 18 Apr 2018 03:55:16 +0000 +Subject: [PATCH] wintrust: Add parameter check in WTHelperGetProvCertFromChain + +Signed-off-by: Alistair Leslie-Hughes +--- + dlls/wintrust/wintrust_main.c | 2 +- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/dlls/wintrust/wintrust_main.c b/dlls/wintrust/wintrust_main.c +index 58e3ac3..bb52282 100644 +--- a/dlls/wintrust/wintrust_main.c ++++ b/dlls/wintrust/wintrust_main.c +@@ -787,7 +787,7 @@ CRYPT_PROVIDER_CERT * WINAPI WTHelperGetProvCertFromChain( + + TRACE("(%p %ld)\n", pSgnr, idxCert); + +- if (idxCert >= pSgnr->csCertChain || !pSgnr->pasCertChain) ++ if (!pSgnr || idxCert >= pSgnr->csCertChain || !pSgnr->pasCertChain) + return NULL; + cert = &pSgnr->pasCertChain[idxCert]; + TRACE("returning %p\n", cert); +-- +1.9.1 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/definition new file mode 100644 index 000000000..ff58e3995 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wintrust-WTHelperGetProvCertFromChain/definition @@ -0,0 +1 @@ +Fixes: [44061] Check Parameter in WTHelperGetProvCertFromChain diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch new file mode 100644 index 000000000..69bd9809a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/0001-wscript-return-TRUE-for-d-and-u-stub-switches.patch @@ -0,0 +1,33 @@ +From d68918425e249c5c218ee6f0c9418e2e2daf9931 Mon Sep 17 00:00:00 2001 +From: Dmitry Kislyuk +Date: Wed, 28 Apr 2021 09:47:41 -0500 +Subject: [PATCH] wscript: return TRUE for /d and /u stub switches + +Patch by Robert Wilhelm from bug: +https://bugs.winehq.org/show_bug.cgi?id=49905 + +VbsEdit is able to execute scripts with this patch applied. + +Signed-off-by: Dmitry Kislyuk +--- + programs/wscript/main.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/programs/wscript/main.c b/programs/wscript/main.c +index a7005a9289c..6d6e8935149 100644 +--- a/programs/wscript/main.c ++++ b/programs/wscript/main.c +@@ -393,6 +393,10 @@ static BOOL set_host_properties(const WCHAR *prop) + wshInteractive = VARIANT_FALSE; + else if(wcsicmp(prop, L"nologo") == 0) + WINE_FIXME("ignored %s switch\n", debugstr_w(L"nologo")); ++ else if(wcsicmp(prop, L"d") == 0) ++ WINE_FIXME("ignored %s switch\n", debugstr_w(L"d")); ++ else if(wcsicmp(prop, L"u") == 0) ++ WINE_FIXME("ignored %s switch\n", debugstr_w(L"u")); + else + { + WINE_FIXME("unsupported switch %s\n", debugstr_w(prop)); +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/definition new file mode 100644 index 000000000..8ae3cf62d --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/wscript-support-d-u-switches/definition @@ -0,0 +1 @@ +Fixes: [49905] wscript: return TRUE for /d and /u stub switches diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch new file mode 100644 index 000000000..bf00b0bdf --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/0001-x3daudio1_7-Create-import-library.patch @@ -0,0 +1,23 @@ +From 9c11f23079351f107a3bbcd3cd274f0a5656518f Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Thu, 1 Oct 2020 18:37:06 +1000 +Subject: [PATCH] x3daudio1_7: Create import library + +--- + dlls/x3daudio1_7/Makefile.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/x3daudio1_7/Makefile.in b/dlls/x3daudio1_7/Makefile.in +index c6a8ed5102a..323d3fad60a 100644 +--- a/dlls/x3daudio1_7/Makefile.in ++++ b/dlls/x3daudio1_7/Makefile.in +@@ -1,5 +1,6 @@ + EXTRADEFS = -DX3DAUDIO1_VER=7 -DXAUDIO2_VER=7 + MODULE = x3daudio1_7.dll ++IMPORTLIB = x3daudio1_7 + PARENTSRC = ../xaudio2_7 + IMPORTS = $(FAUDIO_PE_LIBS) + EXTRAINCL = $(FAUDIO_PE_CFLAGS) +-- +2.33.0 + diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/definition b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/definition new file mode 100644 index 000000000..2764d366c --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/staging-hotfixes/80/xactengine-initial/definition @@ -0,0 +1,5 @@ +Fixes: [31476] Support Bully Scholarship Edition xactengine3_1.dll. +Fixes: [38615] DSA: Drakensang Demo fails on IXACTEngine::Initialize +Fixes: [41030] Pac-Man Museum requires xactengine3_7 +Fixes: [41045] Captain Morgane requires xactengine3_4 +Fixes: [48684] BlazBlue: Calamity Trigger requires for xactengine 3.3 interface. diff --git a/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle index 6619bfa8b..8bb43b6df 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle +++ b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle @@ -3,8 +3,10 @@ # CSMT toggle patch - Corrects the CSMT toggle to be more logical if [ "$_CSMT_toggle" = "true" ] && [ "$_use_staging" = "true" ]; then cd "${srcdir}"/"${_stgsrcdir}" - if git merge-base --is-ancestor c1b4af92f74d7bd330003d39d0bb1f966fdb70a9 HEAD; then + if git merge-base --is-ancestor 22d9eec489d0e0020e41e965bf96871e35f51584 HEAD; then _patchname='CSMT-toggle.patch' && _patchmsg="Applied CSMT toggle logic patch" && nonuser_patcher + elif git merge-base --is-ancestor c1b4af92f74d7bd330003d39d0bb1f966fdb70a9 HEAD; then + _patchname='CSMT-toggle-22d9eec.patch' && _patchmsg="Applied CSMT toggle logic patch" && nonuser_patcher elif git merge-base --is-ancestor ef11bb63ce54490bc5a1e14ab650207b515340da HEAD; then _patchname='CSMT-toggle-c1b4af9.patch' && _patchmsg="Applied CSMT toggle logic patch" && nonuser_patcher elif git merge-base --is-ancestor 5e685d6df972b658fba296dafb5db189af73c7d5 HEAD; then diff --git a/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle.patch b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle.patch index 2259491f2..31965205b 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/CSMT-toggle.patch @@ -57,7 +57,7 @@ index 00000000..6ff97674 ++ // since we want this toggle to disable upstream's CSMT ++ // flip existing csmt dword, returning false if not set. ++ BOOL ret = buf ? !*buf : FALSE; -+ HeapFree(GetProcessHeap(), 0, buf); ++ free(buf); + return ret; + } + static void csmt_set(BOOL status) diff --git a/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/legacy/CSMT-toggle-22d9eec.patch b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/legacy/CSMT-toggle-22d9eec.patch new file mode 100644 index 000000000..2259491f2 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/CSMT-toggle/legacy/CSMT-toggle-22d9eec.patch @@ -0,0 +1,114 @@ +From f31cbcf4cb8fe1285091d8ab053febb115fc08af Mon Sep 17 00:00:00 2001 +From: Firerat +Date: Sat, 7 Apr 2018 01:05:29 +0100 +Subject: [PATCH] winecfg-Staging: Toggle upstream CSMT implementation + + Logic on the toggle is now inverted + 'on' disables upstream's CSMT + 'off' lets upstream wine use its default +--- + ...necfg-Toggle-upstream-CSMT-implementation.patch | 95 ++++++++++++++++++++++ + 1 file changed, 95 insertions(+) + create mode 100644 patches/winecfg-Staging/0006-winecfg-Toggle-upstream-CSMT-implementation.patch + +diff --git a/patches/winecfg-Staging/0006-winecfg-Toggle-upstream-CSMT-implementation.patch b/patches/winecfg-Staging/0006-winecfg-Toggle-upstream-CSMT-implementation.patch +new file mode 100644 +index 00000000..6ff97674 +--- /dev/null ++++ b/patches/winecfg-Staging/0006-winecfg-Toggle-upstream-CSMT-implementation.patch +@@ -0,0 +1,95 @@ ++From 635a7d18629ae7498648193b40de5fddd23ca2cb Mon Sep 17 00:00:00 2001 ++From: Firerat ++Date: Fri, 6 Apr 2018 23:50:21 +0100 ++Subject: [PATCH] winecfg: Toggle upstream CSMT implementation ++ ++ Logic on the toggle is now inverted ++ 'on' disables upstream's CSMT ++ 'off' lets upstream wine use its default ++--- ++ programs/winecfg/resource.h | 2 +- ++ programs/winecfg/staging.c | 22 +++++++++++++++++----- ++ programs/winecfg/winecfg.rc | 2 +- ++ 3 files changed, 19 insertions(+), 7 deletions(-) ++ ++diff --git a/programs/winecfg/resource.h b/programs/winecfg/resource.h ++index b94e773b60..98d7ba152b 100644 ++--- a/programs/winecfg/resource.h +++++ b/programs/winecfg/resource.h ++@@ -213,7 +213,7 @@ ++ #define IDC_SYSPARAMS_MENUBAR 8431 ++ ++ /* Staging tab */ ++-#define IDC_ENABLE_CSMT 9001 +++#define IDC_DISABLE_CSMT 9001 ++ #define IDC_ENABLE_VAAPI 9002 ++ #define IDC_ENABLE_EAX 9003 ++ #define IDC_ENABLE_HIDEWINE 9004 ++diff --git a/programs/winecfg/staging.c b/programs/winecfg/staging.c ++index df9bf0f1ad..51f213f5cb 100644 ++--- a/programs/winecfg/staging.c +++++ b/programs/winecfg/staging.c ++@@ -36,13 +36,25 @@ ++ static BOOL csmt_get(void) ++ { ++ WCHAR *buf = get_reg_key(config_key, L"Direct3D", L"csmt", NULL); ++- BOOL ret = buf ? !!*buf : TRUE; +++ // (dword=0 csmt=off, dword=1 csmt=on ) +++ // since we want this toggle to disable upstream's CSMT +++ // flip existing csmt dword, returning false if not set. +++ BOOL ret = buf ? !*buf : FALSE; ++ HeapFree(GetProcessHeap(), 0, buf); ++ return ret; ++ } ++ static void csmt_set(BOOL status) ++ { ++- set_reg_key_dword(config_key, L"Direct3D", L"csmt", status); +++ if (status) +++ { +++ // TRUE, we disable upstream's csmt by setting dword to 0 +++ set_reg_key_dword(config_key, L"Direct3D", L"csmt", 0); +++ } +++ else +++ { +++ // FALSE, we remove the csmt key letting wine use its default +++ set_reg_key(config_key, "Direct3D", L"csmt", NULL); +++ } ++ } ++ ++ /* ++@@ -123,7 +135,7 @@ static void gtk3_set(BOOL status) ++ ++ static void load_staging_settings(HWND dialog) ++ { ++- CheckDlgButton(dialog, IDC_ENABLE_CSMT, csmt_get() ? BST_CHECKED : BST_UNCHECKED); +++ CheckDlgButton(dialog, IDC_DISABLE_CSMT, csmt_get() ? BST_CHECKED : BST_UNCHECKED); ++ CheckDlgButton(dialog, IDC_ENABLE_VAAPI, vaapi_get() ? BST_CHECKED : BST_UNCHECKED); ++ CheckDlgButton(dialog, IDC_ENABLE_EAX, eax_get() ? BST_CHECKED : BST_UNCHECKED); ++ CheckDlgButton(dialog, IDC_ENABLE_HIDEWINE, hidewine_get() ? BST_CHECKED : BST_UNCHECKED); ++@@ -160,8 +172,8 @@ INT_PTR CALLBACK StagingDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPar ++ if (HIWORD(wParam) != BN_CLICKED) break; ++ switch (LOWORD(wParam)) ++ { ++- case IDC_ENABLE_CSMT: ++- csmt_set(IsDlgButtonChecked(hDlg, IDC_ENABLE_CSMT) == BST_CHECKED); +++ case IDC_DISABLE_CSMT: +++ csmt_set(IsDlgButtonChecked(hDlg, IDC_DISABLE_CSMT) == BST_CHECKED); ++ SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0); ++ return TRUE; ++ case IDC_ENABLE_VAAPI: ++diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc ++index f838a04a4e..8cd5b391ae 100644 ++--- a/programs/winecfg/winecfg.rc +++++ b/programs/winecfg/winecfg.rc ++@@ -315,7 +315,7 @@ FONT 8, "MS Shell Dlg" ++ BEGIN ++ GROUPBOX "Staging settings",IDC_STATIC,8,4,244,210 ++ LTEXT "The following settings are experimental and may break stuff!\nMake sure to reset them again in case of a problem.",IDC_STATIC,16,16,230,16 ++- CONTROL "Enable &CSMT for better graphic performance (deprecated)",IDC_ENABLE_CSMT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,40,230,8 +++ CONTROL "Disable upstream &CSMT (not recommended)",IDC_DISABLE_CSMT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,40,230,8 ++ CONTROL "Enable &VAAPI as backend for DXVA2 GPU decoding",IDC_ENABLE_VAAPI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,55,230,8 ++ CONTROL "Enable Environmental Audio E&xtensions (EAX)",IDC_ENABLE_EAX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,70,230,8 ++ CONTROL "&Hide Wine version from applications",IDC_ENABLE_HIDEWINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,85,230,8 ++-- ++2.17.0 ++ diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/OPWR-proton.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/OPWR-proton.patch index 2d1d9f6c5..33ab3d0b2 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/childwindow/OPWR-proton.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/OPWR-proton.patch @@ -279,14 +279,14 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index e969c3a408a..77adec8423e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -664,6 +664,7 @@ extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; - extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; - extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; -+extern BOOL wine_vk_direct_window_draw( HWND hwnd ) DECLSPEC_HIDDEN; - extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; - - extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -664,6 +664,7 @@ extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + extern Window wine_vk_active_surface( HWND hwnd ); ++extern BOOL wine_vk_direct_window_draw( HWND hwnd ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); @@ -904,4 +905,6 @@ static inline BOOL is_window_rect_mapped( const RECT *rect ) return (p - dst) * sizeof(WCHAR); } diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow index ccb89f1ae..857850823 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow @@ -5,8 +5,10 @@ if ( [ "$_proton_fs_hack" != "true" ] && git merge-base --is-ancestor 0f972e2247932f255f131792724e4796b4b2b87a HEAD ) || ( ! git merge-base --is-ancestor 0f972e2247932f255f131792724e4796b4b2b87a HEAD ); then if git merge-base --is-ancestor 011fabb2c43d13402ea18b6ea7be3669b5e6c7a8 HEAD; then _staging_args+=(-W Pipelight -W winex11-Vulkan_support) - if git merge-base --is-ancestor bca1b7f2faeb0798f4af420c15ff5a1b1f7b40af HEAD; then + if git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then _patchname='childwindow.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher + elif git merge-base --is-ancestor bca1b7f2faeb0798f4af420c15ff5a1b1f7b40af HEAD; then + _patchname='childwindow-c14de4c.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher else _patchname='childwindow-bca1b7f.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher fi diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton index 10bd39e8d..1ee809488 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton @@ -2,8 +2,14 @@ # Standalone child window support for vk - Fixes World of Final Fantasy and others - https://bugs.winehq.org/show_bug.cgi?id=45277 - legacy patchset for older trees applied at an earlier stage in the script if ( [ "$_childwindow_fix" = "true" ] && [ "$_proton_fs_hack" != "true" ] && [ "$_use_staging" = "true" ] ); then - if git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD; then + if git merge-base --is-ancestor c0042af5cc2f6d03e3875f4cd4a7c97315dd0ab2 HEAD; then _patchname='childwindow-proton.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher + elif git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then + _patchname='childwindow-proton-c0042af.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher + elif git merge-base --is-ancestor a6bc5f34b87393e04ff46659f518f2e7094cc7e4 HEAD; then + _patchname='childwindow-proton-c14de4c.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher + elif git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD; then + _patchname='childwindow-proton-a6bc5f3.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher elif git merge-base --is-ancestor c86955d3806879fc97b127730e9fb90e232710a7 HEAD; then _patchname='childwindow-proton-ad5cb83.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher elif git merge-base --is-ancestor a25519ecc673c6c62d9fe606eeac249e4ac55140 HEAD; then @@ -23,9 +29,23 @@ fi fi + if ( [ "$_childwindow_fix" = "true" ] && [ "$_proton_fs_hack" != "true" ] && [ "$_use_staging" != "true" ] ); then + if git merge-base --is-ancestor c0042af5cc2f6d03e3875f4cd4a7c97315dd0ab2 HEAD; then + _patchname='childwindow-proton-mainline.patch' && _patchmsg="Applied child window for vk patch (mainline)" && nonuser_patcher + elif git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then + _patchname='childwindow-proton-mainline-c0042af.patch' && _patchmsg="Applied child window for vk patch (mainline)" && nonuser_patcher + elif git merge-base --is-ancestor a6bc5f34b87393e04ff46659f518f2e7094cc7e4 HEAD; then + _patchname='childwindow-proton-mainline-c14de4c.patch' && _patchmsg="Applied child window for vk patch (mainline)" && nonuser_patcher + elif git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD; then + _patchname='childwindow-proton-mainline-a6bc5f3.patch' && _patchmsg="Applied child window for vk patch (mainline)" && nonuser_patcher + fi + fi + if ( [ "$_childwindow_fix" = "true" ] && [ "$_proton_fs_hack" != "true" ] && [ "$_use_staging" = "true" ] && [ "$_protonify" = "true" ] ); then - if git merge-base --is-ancestor 4390b0117633716b6e5477a35c13f6eb0fd52eff HEAD; then + if git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then _patchname='OPWR-proton.patch' && _patchmsg="Applied other process window Vulkan rendering patchset" && nonuser_patcher + elif git merge-base --is-ancestor 4390b0117633716b6e5477a35c13f6eb0fd52eff HEAD; then + _patchname='OPWR-proton-c14de4c.patch' && _patchmsg="Applied other process window Vulkan rendering patchset" && nonuser_patcher elif git merge-base --is-ancestor c86955d3806879fc97b127730e9fb90e232710a7 HEAD; then _patchname='OPWR-proton-4390b01.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher fi diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton-mainline.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton-mainline.patch new file mode 100644 index 000000000..5f0157d5f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton-mainline.patch @@ -0,0 +1,2079 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + # functions for which a user driver entry must be generated + USER_DRIVER_FUNCS = { + "vkCreateInstance", ++ "vkAcquireNextImageKHR", ++ "vkAcquireNextImage2KHR", + "vkCreateSwapchainKHR", + "vkCreateWin32SurfaceKHR", + "vkDestroyInstance", +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,6 +333,6 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE /* present the drawable on screen */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR host_surface; ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR host_surface; ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray; + extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; ++extern BOOL use_xfixes; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +@@ -433,6 +434,7 @@ extern int primary_monitor; + extern int copy_default_colors; + extern int alloc_system_colors; + extern int xrender_error_base; ++extern int xfixes_event_base; + extern char *process_name; + extern Display *clipboard_display; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; + extern BOOL use_xfixes; ++extern BOOL use_xpresent; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void); + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); +-extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); ++extern Window wine_vk_active_surface( HWND hwnd ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) + extern void make_window_embedded( struct x11drv_win_data *data ); + extern Window create_dummy_client_window(void); + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ); ++extern void update_client_window( HWND hwnd ); + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); + extern void change_systray_owner( Display *display, Window systray_window ); + extern HWND create_foreign_window( Display *display, Window window ); +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR host_surface; + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton.patch index 5031312e8..471ca7710 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/childwindow-proton.patch @@ -114,14 +114,14 @@ index 2eeff42c56e..d905412ca0d 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { - "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, - - # VK_KHR_swapchain -+ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, -+ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, - "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, - "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, - "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + # functions for which a user driver entry must be generated + USER_DRIVER_FUNCS = { + "vkCreateInstance", ++ "vkAcquireNextImageKHR", ++ "vkAcquireNextImage2KHR", + "vkCreateSwapchainKHR", + "vkCreateWin32SurfaceKHR", + "vkDestroyInstance", diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 743a964c44c..71ab92297d2 100644 --- a/dlls/winex11.drv/vulkan.c @@ -429,7 +429,7 @@ index 71ab92297d2..210c7989b3c 100644 @@ -63,6 +64,8 @@ struct wine_vk_surface struct list entry; Window window; - VkSurfaceKHR surface; /* native surface */ + VkSurfaceKHR host_surface; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; HWND hwnd; @@ -615,14 +615,14 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 537b8d12704..2703322c981 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; - extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; - extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; -+extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; - - extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); -- 2.37.1 @@ -651,7 +651,7 @@ index 210c7989b3c..34b1be5c61b 100644 @@ -64,6 +64,7 @@ struct wine_vk_surface struct list entry; Window window; - VkSurfaceKHR surface; /* native surface */ + VkSurfaceKHR host_surface; + VkPresentModeKHR present_mode; BOOL offscreen; /* drawable is offscreen */ HDC hdc; @@ -834,21 +834,21 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 890770c5248..830bf11ae4c 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -426,6 +426,7 @@ extern BOOL show_systray DECLSPEC_HIDDEN; - extern BOOL grab_pointer DECLSPEC_HIDDEN; - extern BOOL grab_fullscreen DECLSPEC_HIDDEN; - extern BOOL usexcomposite DECLSPEC_HIDDEN; -+extern BOOL use_xfixes DECLSPEC_HIDDEN; - extern BOOL managed_mode DECLSPEC_HIDDEN; - extern BOOL decorated_mode DECLSPEC_HIDDEN; - extern BOOL private_color_map DECLSPEC_HIDDEN; -@@ -433,6 +434,7 @@ extern int primary_monitor DECLSPEC_HIDDEN; - extern int copy_default_colors DECLSPEC_HIDDEN; - extern int alloc_system_colors DECLSPEC_HIDDEN; - extern int xrender_error_base DECLSPEC_HIDDEN; -+extern int xfixes_event_base DECLSPEC_HIDDEN; - extern char *process_name DECLSPEC_HIDDEN; - extern Display *clipboard_display DECLSPEC_HIDDEN; +@@ -426,6 +426,7 @@ extern BOOL show_systray; + extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; ++extern BOOL use_xfixes; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +@@ -433,6 +434,7 @@ extern int primary_monitor; + extern int copy_default_colors; + extern int alloc_system_colors; + extern int xrender_error_base; ++extern int xfixes_event_base; + extern char *process_name; + extern Display *clipboard_display; extern WNDPROC client_foreign_window_proc; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 20e07679635..461a9d6fe6e 100644 @@ -986,7 +986,7 @@ index 00000000000..3ab31201d3d + +#ifdef SONAME_LIBXFIXES +#include -+#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; +MAKE_FUNCPTR(XFixesQueryExtension) +MAKE_FUNCPTR(XFixesQueryVersion) +MAKE_FUNCPTR(XFixesSelectSelectionInput) @@ -1144,14 +1144,14 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 830bf11ae4c..eb38909a47d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -427,6 +427,7 @@ extern BOOL grab_pointer DECLSPEC_HIDDEN; - extern BOOL grab_fullscreen DECLSPEC_HIDDEN; - extern BOOL usexcomposite DECLSPEC_HIDDEN; - extern BOOL use_xfixes DECLSPEC_HIDDEN; -+extern BOOL use_xpresent DECLSPEC_HIDDEN; - extern BOOL managed_mode DECLSPEC_HIDDEN; - extern BOOL decorated_mode DECLSPEC_HIDDEN; - extern BOOL private_color_map DECLSPEC_HIDDEN; +@@ -427,6 +427,7 @@ extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; + extern BOOL use_xfixes; ++extern BOOL use_xpresent; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 461a9d6fe6e..92897c16238 100644 --- a/dlls/winex11.drv/x11drv_main.c @@ -1261,7 +1261,7 @@ index 3ab31201d3d..80f8cca04e4 100644 --- a/dlls/winex11.drv/xfixes.h +++ b/dlls/winex11.drv/xfixes.h @@ -29,6 +29,9 @@ - #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f; MAKE_FUNCPTR(XFixesQueryExtension) MAKE_FUNCPTR(XFixesQueryVersion) +MAKE_FUNCPTR(XFixesCreateRegion) @@ -1304,7 +1304,7 @@ index 00000000000..6fd813a930e + +#ifdef SONAME_LIBXPRESENT +#include -+#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; +MAKE_FUNCPTR(XPresentQueryExtension) +MAKE_FUNCPTR(XPresentQueryVersion) +MAKE_FUNCPTR(XPresentPixmap) @@ -1456,14 +1456,14 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index eb38909a47d..5beaf0d10fc 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; - extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; - extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; --extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; -+extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; - extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void); + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); +-extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); -- 2.37.1 @@ -1512,7 +1512,7 @@ index 8438fdb7225..b6087eac24c 100644 @@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); - XConfigureWindow( data->display, data->client_window, mask, &changes ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); + resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); } } @@ -1521,14 +1521,14 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5beaf0d10fc..801ec79559a 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; - extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; - extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; - extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; -+extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; - extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; - - extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); -- 2.37.1 @@ -1628,22 +1628,22 @@ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 801ec79559a..c13bc0bdb16 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h -@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; - extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; - extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; - extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; -+extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; - extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; - - extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; -@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) DECLSPEC_HIDDEN - extern void make_window_embedded( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; - extern Window create_dummy_client_window(void) DECLSPEC_HIDDEN; - extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ) DECLSPEC_HIDDEN; -+extern void update_client_window( HWND hwnd ) DECLSPEC_HIDDEN; - extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ) DECLSPEC_HIDDEN; - extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; - extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); ++extern Window wine_vk_active_surface( HWND hwnd ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) + extern void make_window_embedded( struct x11drv_win_data *data ); + extern Window create_dummy_client_window(void); + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ); ++extern void update_client_window( HWND hwnd ); + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); + extern void change_systray_owner( Display *display, Window systray_window ); + extern HWND create_foreign_window( Display *display, Window window ); -- 2.37.1 @@ -1663,7 +1663,7 @@ index 2770123deba..77fd1b5ef66 100644 +++ b/dlls/winex11.drv/vulkan.c @@ -64,6 +64,7 @@ struct wine_vk_surface Window window; - VkSurfaceKHR surface; /* native surface */ + VkSurfaceKHR host_surface; VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ BOOL offscreen; /* drawable is offscreen */ @@ -2048,7 +2048,7 @@ index e1d84c61575..fa22dd890d1 100644 + if (data->client_window && data->whole_window && old_active != data->client_window) + { + TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); -+ XReparentWindow( data->display, data->client_window, data->whole_window, ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, + data->client_rect.left - data->whole_rect.left, + data->client_rect.top - data->whole_rect.top ); + } diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/OPWR-proton-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/OPWR-proton-c14de4c.patch new file mode 100644 index 000000000..2d1d9f6c5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/OPWR-proton-c14de4c.patch @@ -0,0 +1,563 @@ +From eb0814df6547cbd72292ce889e1626ac95f10762 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sun, 22 May 2022 12:53:05 -0500 +Subject: [PATCH] winex11.drv: Get window DC each time for blitting. + +Keeping it since the surface creation doesn't look right, +it may affect app's GDI operations and window may be deleted +while the surface stays valid. + +CW-Bug-Id: #20680 +--- + dlls/winex11.drv/vulkan.c | 14 ++++++-------- + 1 file changed, 6 insertions(+), 8 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 22d50cf91d6..19b46014021 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -70,7 +70,6 @@ struct wine_vk_surface + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + LONG swapchain_count; /* surface can have one active an many retired swapchains */ +- HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -244,10 +243,8 @@ void wine_vk_surface_destroy(struct wine_vk_surface *surface) + XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); + XSync(gdi_display, False); + +- if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); + surface->hwnd_thread_id = 0; + surface->hwnd = 0; +- surface->hdc = 0; + wine_vk_surface_release(surface); + } + +@@ -413,10 +410,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) +- { + wine_vk_surface_grab(surface); +- hdc = surface->hdc; +- } + pthread_mutex_unlock(&vulkan_mutex); + + if (!surface || !surface->offscreen) +@@ -436,7 +430,11 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) ++ ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && surface && surface->offscreen) ++ hdc = NtUserGetDCEx(surface->hwnd, 0, DCX_USESTYLE | DCX_CACHE); ++ ++ if (hdc) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +@@ -445,6 +443,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); + if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); ++ NtUserReleaseDC(surface->hwnd, hdc); + } + + if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); +@@ -532,7 +531,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { +- x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +From 191b54b79d1a31c69e7d425f94c4da519f583775 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 18:45:15 -0500 +Subject: [PATCH] winex11.drv: HACK: Add an option to support GDI blits from + unmapped Vulkan windows. + +And enable it for Disgaea 5 Complete. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/vulkan.c | 90 +++++++++++++++++++++++++++++----- + dlls/winex11.drv/window.c | 7 +++ + dlls/winex11.drv/x11drv.h | 3 + + dlls/winex11.drv/x11drv_main.c | 8 +++ + 4 files changed, 96 insertions(+), 12 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 19b46014021..423a89c0298 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -72,6 +72,7 @@ struct wine_vk_surface + LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HWND hwnd; + DWORD hwnd_thread_id; ++ BOOL gdi_blit_source; /* HACK: gdi blits from the window should work with Vulkan rendered contents. */ + }; + + typedef struct VkXlibSurfaceCreateInfoKHR +@@ -315,7 +316,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + if (surface->hwnd != hwnd) continue; + if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); +- else wine_vk_surface_set_offscreen(surface, known_child); ++ else wine_vk_surface_set_offscreen(surface, known_child || surface->gdi_blit_source); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -330,7 +331,7 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (!surface->swapchain_count) continue; ++ if (!surface->swapchain_count || surface->gdi_blit_source) continue; + active = surface; + surface_with_swapchain_count++; + } +@@ -347,6 +348,25 @@ Window wine_vk_active_surface(HWND hwnd) + return window; + } + ++BOOL wine_vk_direct_window_draw( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface; ++ BOOL ret = FALSE; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->gdi_blit_source) ++ { ++ ret = TRUE; ++ break; ++ } ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ return ret; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -396,6 +416,17 @@ static VkResult X11DRV_create_vk_instance_with_callback(const VkInstanceCreateIn + return res; + } + ++static void set_dc_drawable( HDC hdc, Drawable drawable, const RECT *rect ) ++{ ++ struct x11drv_escape_set_drawable escape; ++ ++ escape.code = X11DRV_SET_DRAWABLE; ++ escape.mode = IncludeInferiors; ++ escape.drawable = drawable; ++ escape.dc_rect = *rect; ++ NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); ++} ++ + static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) +@@ -403,10 +434,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; ++ DWORD dc_flags = DCX_USESTYLE; + VkResult result; + VkFence orig_fence; + BOOL wait_fence = FALSE; + HDC hdc = 0; ++ RECT rect; + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) +@@ -432,17 +465,29 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + + if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && surface && surface->offscreen) +- hdc = NtUserGetDCEx(surface->hwnd, 0, DCX_USESTYLE | DCX_CACHE); ++ { ++ if (!surface->gdi_blit_source) ++ dc_flags |= DCX_CACHE; ++ hdc = NtUserGetDCEx(surface->hwnd, 0, dc_flags); ++ } + + if (hdc) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); +- escape.code = X11DRV_PRESENT_DRAWABLE; +- escape.drawable = surface->window; +- escape.flush = TRUE; +- NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) +- if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); ++ if (surface->gdi_blit_source) ++ { ++ NtUserGetClientRect( surface->hwnd, &rect ); ++ set_dc_drawable( hdc, surface->window, &rect ); ++ } ++ else ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); ++ } + NtUserReleaseDC(surface->hwnd, hdc); + } + +@@ -548,10 +593,24 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + +- if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || +- NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ if (vulkan_gdi_blit_source_hack) ++ { ++ RECT rect; ++ ++ NtUserGetWindowRect( create_info->hwnd, &rect ); ++ if (!is_window_rect_mapped( &rect )) ++ { ++ FIXME("HACK: setting gdi_blit_source for hwnd %p, surface %p.\n", x11_surface->hwnd, x11_surface); ++ x11_surface->gdi_blit_source = TRUE; ++ XReparentWindow( gdi_display, x11_surface->window, get_dummy_parent(), 0, 0 ); ++ } ++ } ++ ++ x11_surface->known_child = create_info->hwnd && (NtUserGetWindowRelative( create_info->hwnd, GW_CHILD ) ++ || NtUserGetAncestor( create_info->hwnd, GA_PARENT ) != NtUserGetDesktopWindow()); ++ ++ if (x11_surface->known_child || x11_surface->gdi_blit_source) + { +- x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -578,6 +638,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + *surface = (uintptr_t)x11_surface; + ++ if (x11_surface->gdi_blit_source) ++ { ++ /* Make sure window gets surface destroyed. */ ++ NtUserSetWindowPos( x11_surface->hwnd, 0, 0, 0, 0, 0, ++ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | ++ SWP_NOREDRAW | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_STATECHANGED ); ++ } + TRACE("Created surface=0x%s\n", wine_dbgstr_longlong(*surface)); + return VK_SUCCESS; + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8fc05079d76..8b115ba3faf 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2788,6 +2788,13 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + if (!data->whole_window && !data->embedded) goto done; + if (swp_flags & SWP_HIDEWINDOW) goto done; + if (data->use_alpha) goto done; ++ ++ if (wine_vk_direct_window_draw( hwnd )) ++ { ++ if (*surface) window_surface_release( *surface ); ++ *surface = NULL; ++ goto done; ++ } + if (!get_surface_rect( visible_rect, &surface_rect )) goto done; + + if (*surface) window_surface_release( *surface ); +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e969c3a408a..77adec8423e 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -664,6 +664,7 @@ extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; + extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; ++extern BOOL wine_vk_direct_window_draw( HWND hwnd ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -904,4 +905,6 @@ static inline BOOL is_window_rect_mapped( const RECT *rect ) + return (p - dst) * sizeof(WCHAR); + } + ++extern BOOL vulkan_gdi_blit_source_hack; ++ + #endif /* __WINE_X11DRV_H */ +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index f5aaeab0df3..a57bbfd67fc 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -92,6 +92,7 @@ char *process_name = NULL; + int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; ++BOOL vulkan_gdi_blit_source_hack = FALSE; + + static x11drv_error_callback err_callback; /* current callback for error */ + static Display *err_callback_display; /* display callback is set for */ +@@ -732,6 +733,16 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_InitKeyboard( gdi_display ); + X11DRV_InitMouse( gdi_display ); + if (use_xim) use_xim = xim_init( input_style ); ++ ++ { ++ const char *sgi = getenv("SteamGameId"); ++ const char *e = getenv("WINE_VK_GDI_BLIT_SOURCE_HACK"); ++ vulkan_gdi_blit_source_hack = ++ (sgi && ( ++ !strcmp(sgi, "803600") /* Disgaea 5 Complete */ ++ )) || ++ (e && *e != '\0' && *e != '0'); ++ } + + init_user_driver(); + X11DRV_DisplayDevices_Init(FALSE); +From 787920ce256f4df6a202ce47fa193ab040984ec6 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 21 May 2022 20:01:08 -0500 +Subject: [PATCH] winex11.drv: Implement other process window Vulkan rendering. + +CW-Bug-Id: #20680 +--- + dlls/winex11.drv/vulkan.c | 89 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 79 insertions(+), 10 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 423a89c0298..090e7091335 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -73,6 +73,10 @@ struct wine_vk_surface + HWND hwnd; + DWORD hwnd_thread_id; + BOOL gdi_blit_source; /* HACK: gdi blits from the window should work with Vulkan rendered contents. */ ++ BOOL other_process; ++ Colormap client_colormap; ++ HDC draw_dc; ++ unsigned int width, height; + }; + + typedef struct VkXlibSurfaceCreateInfoKHR +@@ -232,8 +236,12 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + pthread_mutex_unlock(&vulkan_mutex); + } + ++ if (surface->draw_dc) ++ NtGdiDeleteObjectApp(surface->draw_dc); + if (surface->window) + XDestroyWindow(gdi_display, surface->window); ++ if (surface->client_colormap) ++ XFreeColormap( gdi_display, surface->client_colormap ); + + free(surface); + } +@@ -357,7 +365,7 @@ BOOL wine_vk_direct_window_draw( HWND hwnd ) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->gdi_blit_source) ++ if (surface->gdi_blit_source && !surface->other_process) + { + ret = TRUE; + break; +@@ -448,7 +456,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; +- else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ else if (surface->other_process || surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; + +@@ -466,7 +474,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && surface && surface->offscreen) + { +- if (!surface->gdi_blit_source) ++ if (!surface->gdi_blit_source || surface->other_process) + dc_flags |= DCX_CACHE; + hdc = NtUserGetDCEx(surface->hwnd, 0, dc_flags); + } +@@ -476,8 +484,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + if (surface->gdi_blit_source) + { ++ unsigned int width, height; ++ + NtUserGetClientRect( surface->hwnd, &rect ); +- set_dc_drawable( hdc, surface->window, &rect ); ++ if (surface->other_process) ++ { ++ width = max( rect.right - rect.left, 1 ); ++ height = max( rect.bottom - rect.top, 1 ); ++ if (!NtGdiStretchBlt(hdc, rect.left, rect.top, width, ++ height, surface->draw_dc, 0, 0, ++ width, height, SRCCOPY, 0)) ++ ERR("NtGdiStretchBlt failed.\n"); ++ if (width != surface->width || height != surface->height) ++ { ++ TRACE("Resizing.\n"); ++ XMoveResizeWindow( gdi_display, surface->window, 0, 0, width, height); ++ set_dc_drawable( surface->draw_dc, surface->window, &rect ); ++ surface->width = width; ++ surface->height = height; ++ } ++ } ++ else ++ { ++ set_dc_drawable( hdc, surface->window, &rect ); ++ } + } + else + { +@@ -530,8 +560,10 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.surface = x11_surface->surface; + + /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && +- !(use_xpresent && use_xfixes && usexcomposite)) ++ if (x11_surface->gdi_blit_source) ++ create_info_host.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; ++ else if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +@@ -560,6 +592,8 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; + struct wine_vk_surface *x11_surface; ++ DWORD hwnd_pid; ++ RECT rect; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -576,8 +610,43 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { +- x11_surface->window = create_client_window(create_info->hwnd, &default_visual); +- x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); ++ x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, &hwnd_pid); ++ if (x11_surface->hwnd_thread_id && hwnd_pid != GetCurrentProcessId()) ++ { ++ XSetWindowAttributes attr; ++ ++ WARN("Other process window %p.\n", x11_surface->hwnd); ++ NtUserGetClientRect( x11_surface->hwnd, &rect ); ++ x11_surface->width = max( rect.right - rect.left, 1 ); ++ x11_surface->height = max( rect.bottom - rect.top, 1 ); ++ x11_surface->client_colormap = XCreateColormap( gdi_display, get_dummy_parent(), default_visual.visual, ++ (default_visual.class == PseudoColor || default_visual.class == GrayScale ++ || default_visual.class == DirectColor) ? AllocAll : AllocNone ); ++ attr.colormap = x11_surface->client_colormap; ++ attr.bit_gravity = NorthWestGravity; ++ attr.win_gravity = NorthWestGravity; ++ attr.backing_store = NotUseful; ++ attr.border_pixel = 0; ++ x11_surface->window = XCreateWindow( gdi_display, ++ get_dummy_parent(), ++ 0, 0, x11_surface->width, x11_surface->height, 0, ++ default_visual.depth, InputOutput, ++ default_visual.visual, CWBitGravity | CWWinGravity | ++ CWBackingStore | CWColormap | CWBorderPixel, &attr ); ++ if (x11_surface->window) ++ { ++ XMapWindow( gdi_display, x11_surface->window ); ++ XSync( gdi_display, False ); ++ x11_surface->gdi_blit_source = TRUE; ++ x11_surface->other_process = TRUE; ++ x11_surface->draw_dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL ); ++ set_dc_drawable( x11_surface->draw_dc, x11_surface->window, &rect ); ++ } ++ } ++ else ++ { ++ x11_surface->window = create_client_window(create_info->hwnd, &default_visual); ++ } + } + else + { +@@ -593,7 +662,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + +- if (vulkan_gdi_blit_source_hack) ++ if (!x11_surface->gdi_blit_source && vulkan_gdi_blit_source_hack) + { + RECT rect; + +@@ -638,7 +707,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + *surface = (uintptr_t)x11_surface; + +- if (x11_surface->gdi_blit_source) ++ if (x11_surface->gdi_blit_source && !x11_surface->other_process) + { + /* Make sure window gets surface destroyed. */ + NtUserSetWindowPos( x11_surface->hwnd, 0, 0, 0, 0, 0, +From 46a904624f1c3f62df806e9f0bff2bfda6bdf727 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 22 Jun 2022 13:20:15 -0500 +Subject: [PATCH] winex11.drv: Add WINE_DISABLE_VULKAN_OPWR option. + +CW-Bug-Id: #20680 +--- + dlls/winex11.drv/vulkan.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 090e7091335..2cb9ad949ae 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -23,6 +23,7 @@ + #include "config.h" + + #include ++#include + #include + #include + +@@ -585,6 +586,17 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + return result; + } + ++static BOOL disable_opwr(void) ++{ ++ static int disable = -1; ++ if (disable == -1) ++ { ++ const char *e = getenv("WINE_DISABLE_VULKAN_OPWR"); ++ disable = e && atoi(e); ++ } ++ return disable; ++} ++ + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + const VkWin32SurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSurfaceKHR *surface) +@@ -616,6 +628,14 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + XSetWindowAttributes attr; + + WARN("Other process window %p.\n", x11_surface->hwnd); ++ ++ if (disable_opwr() && x11_surface->hwnd != NtUserGetDesktopWindow()) ++ { ++ ERR("HACK: Failing surface creation for other process window %p.\n", create_info->hwnd); ++ res = VK_ERROR_OUT_OF_HOST_MEMORY; ++ goto err; ++ } ++ + NtUserGetClientRect( x11_surface->hwnd, &rect ); + x11_surface->width = max( rect.right - rect.left, 1 ); + x11_surface->height = max( rect.bottom - rect.top, 1 ); diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-c14de4c.patch new file mode 100644 index 000000000..77f58a7d5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-c14de4c.patch @@ -0,0 +1,1610 @@ +From 99271b005a49e52dd903872983864456d26fdff7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/10] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/vulkan.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 139faf6b407..0baa29b435a 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -54,6 +54,7 @@ static CRITICAL_SECTION_DEBUG critsect_debug = + static CRITICAL_SECTION context_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -138,6 +139,7 @@ static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); + + return TRUE; + +@@ -255,20 +257,28 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { ++ VkResult result; + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + if (!x11_surface->hwnd) + return VK_ERROR_SURFACE_LOST_KHR; + + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ EnterCriticalSection(&context_section); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ LeaveCriticalSection(&context_section); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -366,12 +378,19 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ EnterCriticalSection(&context_section); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ LeaveCriticalSection(&context_section); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.31.0 + +From c53a9587846cb6ad0e08b456de58908d84e39dae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:07:04 +0100 +Subject: [PATCH 02/10] winex11.drv: Keep a list of alive vulkan surfaces. + +And resize them when client rect size changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/vulkan.c | 47 +++++++++++++++++++++++++++++++++------ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 42 insertions(+), 7 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 0baa29b435a..dd75a159ab1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -60,6 +60,7 @@ static XContext vulkan_swapchain_context; + + struct wine_vk_surface + { ++ struct list entry; + LONG ref; + Window window; + VkSurfaceKHR surface; /* native surface */ +@@ -208,6 +209,10 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + if (InterlockedDecrement(&surface->ref)) + return; + ++ EnterCriticalSection(&context_section); ++ list_remove(&surface->entry); ++ LeaveCriticalSection(&context_section); ++ + if (surface->window) + XDestroyWindow(gdi_display, surface->window); + +@@ -215,14 +220,26 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + } + + void wine_vk_surface_destroy(HWND hwnd) ++{ ++ struct list *surface_list; ++ EnterCriticalSection(&context_section); ++ if (XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface_list)) ++ surface_list = NULL; ++ XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); ++ LeaveCriticalSection(&context_section); ++ heap_free(surface_list); ++} ++ ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) + { + struct wine_vk_surface *surface; ++ struct list *surface_list; + EnterCriticalSection(&context_section); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface_list)) + { +- wine_vk_surface_release(surface); ++ LIST_FOR_EACH_ENTRY(surface, surface_list, struct wine_vk_surface, entry) ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + LeaveCriticalSection(&context_section); + } + +@@ -285,7 +302,8 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *prev; ++ struct wine_vk_surface *x11_surface; ++ struct list *surface_list; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -304,6 +322,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + x11_surface->window = x11_surface->hwnd ? create_client_window(create_info->hwnd, &default_visual) + : create_dummy_client_window(); ++ list_init(&x11_surface->entry); + + if (!x11_surface->window) + { +@@ -329,12 +348,23 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (x11_surface->hwnd) + { + EnterCriticalSection(&context_section); +- if (!XFindContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char **)&prev)) ++ if (XFindContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char **)&surface_list) && ++ (surface_list = heap_alloc_zero(sizeof(*surface_list)))) + { +- wine_vk_surface_release(prev); ++ list_init(surface_list); ++ XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)surface_list); + } +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); ++ if (!XFindContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char **)&surface_list)) ++ list_add_tail(surface_list, &x11_surface->entry); + LeaveCriticalSection(&context_section); ++ ++ if (!surface_list) ++ { ++ ERR("Failed to allocate surface list %p\n", create_info->hwnd); ++ res = VK_ERROR_OUT_OF_HOST_MEMORY; ++ goto err; ++ } ++ + } + + *surface = (uintptr_t)x11_surface; +@@ -694,4 +723,8 @@ void wine_vk_surface_destroy(HWND hwnd) + { + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges changes) ++{ ++} ++ + #endif /* SONAME_LIBVULKAN */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index baaa30d74e3..0cc4c0617c4 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1362,6 +1362,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( data->display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 45855976607..bad91f36830 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -594,6 +594,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); + extern Window init_clip_window(void); +-- +2.31.0 + +From 0880e8b4aa1d90a068e264e6dfa928be151a019f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:07:04 +0100 +Subject: [PATCH 03/10] winex11.drv: Check client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/vulkan.c | 31 +++++++++++++++++++++++++------ + dlls/winex11.drv/window.c | 17 +++++++++++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 43 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index dd75a159ab1..780d689fe47 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HWND hwnd; ++ HDC dc; + }; + + typedef struct VkXlibSurfaceCreateInfoKHR +@@ -206,17 +208,32 @@ static struct wine_vk_surface *wine_vk_surface_grab(struct wine_vk_surface *surf + + static void wine_vk_surface_release(struct wine_vk_surface *surface) + { +- if (InterlockedDecrement(&surface->ref)) +- return; ++ struct wine_vk_surface *other = NULL; ++ struct list *surface_list, *entry; ++ HWND hwnd = surface->hwnd; + ++ /* make sure other surface won't destroy itself until we've finished */ + EnterCriticalSection(&context_section); +- list_remove(&surface->entry); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface_list) && ++ ((entry = list_prev(surface_list, &surface->entry)) || (entry = list_next(surface_list, &surface->entry)))) ++ other = wine_vk_surface_grab(LIST_ENTRY(entry, struct wine_vk_surface, entry)); + LeaveCriticalSection(&context_section); + +- if (surface->window) +- XDestroyWindow(gdi_display, surface->window); ++ if (!InterlockedDecrement(&surface->ref)) ++ { ++ ReleaseDC(hwnd, surface->dc); ++ ++ EnterCriticalSection(&context_section); ++ list_remove(&surface->entry); ++ LeaveCriticalSection(&context_section); ++ ++ if (surface->window) ++ destroy_client_window(hwnd, surface->window, other ? other->window : None); ++ ++ heap_free(surface); ++ } + +- heap_free(surface); ++ if (other) wine_vk_surface_release(other); + } + + void wine_vk_surface_destroy(HWND hwnd) +@@ -322,6 +339,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + x11_surface->window = x11_surface->hwnd ? create_client_window(create_info->hwnd, &default_visual) + : create_dummy_client_window(); ++ x11_surface->dc = GetDC(create_info->hwnd); + list_init(&x11_surface->entry); + + if (!x11_surface->window) +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 0cc4c0617c4..84da3606d5b 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1467,6 +1467,23 @@ static Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * destroy_client_window ++ */ ++void destroy_client_window( HWND hwnd, Window old_window, Window new_window ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ if (data->client_window == old_window) data->client_window = new_window; ++ /* make sure any request that could use old_window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++ XDestroyWindow( gdi_display, old_window ); ++} ++ ++ + /********************************************************************** + * create_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index bad91f36830..0aedaa35f97 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -603,6 +603,7 @@ extern void read_net_wm_states( Display *display, struct x11drv_win_data *data ) + extern void update_net_wm_states( struct x11drv_win_data *data ); + extern void make_window_embedded( struct x11drv_win_data *data ); + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ); ++extern void destroy_client_window( HWND hwnd, Window old_window, Window new_window ); + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); + extern void change_systray_owner( Display *display, Window systray_window ); + extern void update_systray_balloon_position(void); +-- +2.31.0 + +From eee3a5e320cb1a7c8e16b837602e6fe76a987a80 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 22 Feb 2021 20:55:05 +0100 +Subject: [PATCH 04/10] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 21ebcc56519..9046ac9d95f 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -569,6 +569,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 66b28bf13df..c49a75746ab 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -208,6 +208,8 @@ FUNCTION_OVERRIDES = { + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_external_fence_capabilities + "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 780d689fe47..e269398d456 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -77,6 +77,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -118,6 +119,7 @@ static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -287,6 +289,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -679,6 +696,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index c2a2acfc282..c36724c80c3 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.31.0 + +From cce417367b62ce4f04b34813e478fd732f72418c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:33:06 +0100 +Subject: [PATCH 05/10] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index d070c0f1171..88ef1e3e2f8 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -232,16 +232,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index fd39b58bb3b..17fcb7b32a3 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1945,20 +1945,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( WindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1966,25 +1966,25 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); ++ if (escape.drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( WindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1992,7 +1992,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); ++ if (escape.drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static const GLubyte *wglGetString(GLenum name) +@@ -3307,15 +3307,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( WindowFromDC( hdc ), hdc ))) +@@ -3336,7 +3336,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3357,10 +3357,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3370,12 +3370,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); ++ if (escape.drawable) ExtEscape( ctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 0aedaa35f97..1830e58a38e 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -286,7 +286,7 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE, /* present the drawable on screen */ + }; + + struct x11drv_escape_set_drawable +@@ -305,10 +305,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.31.0 + +From d330ac15847e91ecda488f784b156465c5087aad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:18:44 +0100 +Subject: [PATCH 06/10] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/vulkan.c | 77 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 15 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 85 insertions(+), 8 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index e269398d456..96b657f93c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -32,6 +32,7 @@ + #include "wine/debug.h" + #include "wine/heap.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -64,6 +65,7 @@ struct wine_vk_surface + LONG ref; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ + HWND hwnd; + HDC dc; + }; +@@ -249,6 +251,31 @@ void wine_vk_surface_destroy(HWND hwnd) + heap_free(surface_list); + } + ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ + void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) + { + struct wine_vk_surface *surface; +@@ -262,6 +289,17 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + LeaveCriticalSection(&context_section); + } + ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ struct list *surface_list; ++ EnterCriticalSection(&context_section); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface_list)) ++ LIST_FOR_EACH_ENTRY(surface, surface_list, struct wine_vk_surface, entry) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ LeaveCriticalSection(&context_section); ++} ++ + static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + const VkAllocationCallbacks *allocator, VkInstance *instance) + { +@@ -293,7 +331,24 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface; ++ VkResult result; ++ ++ EnterCriticalSection(&context_section); ++ if (XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) surface = NULL; ++ LeaveCriticalSection(&context_section); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ ExtEscape(surface->dc, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -344,13 +399,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && GetAncestor(create_info->hwnd, GA_PARENT) != GetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = heap_alloc_zero(sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -370,6 +418,15 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (GetWindow( create_info->hwnd, GW_CHILD ) || GetAncestor( create_info->hwnd, GA_PARENT ) != GetDesktopWindow()) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +@@ -765,4 +822,8 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges chang + { + } + ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++} ++ + #endif /* SONAME_LIBVULKAN */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 84da3606d5b..f1c7f8ec388 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1727,6 +1727,15 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = GetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (GetWindow( parent, GW_CHILD ) || GetAncestor( parent, GA_PARENT ) != GetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == GetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1753,6 +1762,10 @@ void CDECL X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = GetAncestor( hwnd, GA_PARENT ); ++ ++ if (!GetWindow( parent, GW_CHILD ) && GetAncestor( parent, GA_PARENT ) == GetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1958,6 +1971,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2296,6 +2310,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 1830e58a38e..166d50c20ad 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -595,6 +595,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); + extern Window init_clip_window(void); +-- +2.31.0 + +From 2841e0a5b2025d9a13fbf8c1579cf393ff5f1b39 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:30:04 +0100 +Subject: [PATCH 07/10] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 96b657f93c2..fd0e47233fd 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -65,6 +65,7 @@ struct wine_vk_surface + LONG ref; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HWND hwnd; + HDC dc; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -142,6 +146,9 @@ static BOOL WINAPI wine_vk_init(INIT_ONCE *once, void *param, void **context) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -331,23 +338,46 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + + EnterCriticalSection(&context_section); + if (XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) surface = NULL; + LeaveCriticalSection(&context_section); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + ExtEscape(surface->dc, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + return result; + } + +@@ -375,6 +405,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.31.0 + +From 4e89def87b7c3a6f75e6853efb8e8698c381deea Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 08/10] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 88ef1e3e2f8..cdb3dd931e9 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -224,14 +224,6 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_GET_DRAWABLE: +- if (out_count >= sizeof(struct x11drv_escape_get_drawable)) +- { +- struct x11drv_escape_get_drawable *data = out_data; +- data->drawable = physDev->drawable; +- return TRUE; +- } +- break; + case X11DRV_PRESENT_DRAWABLE: + if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 166d50c20ad..565b0dedbb1 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -283,7 +283,6 @@ extern int *get_window_surface_mapping( int bpp, int *mapping ); + enum x11drv_escape_codes + { + X11DRV_SET_DRAWABLE, /* set current drawable for a DC */ +- X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ + X11DRV_PRESENT_DRAWABLE, /* present the drawable on screen */ +@@ -297,14 +296,6 @@ struct x11drv_escape_set_drawable + RECT dc_rect; /* DC rectangle relative to drawable */ + }; + +-struct x11drv_escape_get_drawable +-{ +- enum x11drv_escape_codes code; /* escape code (X11DRV_GET_DRAWABLE) */ +- Drawable drawable; /* X drawable */ +- Drawable gl_drawable; /* GL drawable */ +- int pixel_format; /* internal GL pixel format */ +-}; +- + struct x11drv_escape_present_drawable + { + enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ +-- +2.31.0 + +From f693181afe52898711b3edce27793f8268bd4e8e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 00:10:49 +0100 +Subject: [PATCH 09/10] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index b511af3f691..1f6508df034 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -79,6 +79,7 @@ + #include + + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -198,7 +199,6 @@ static UINT rendered_formats; + static ULONG64 last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -1977,28 +1977,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2012,7 +1990,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 565b0dedbb1..73926b23ab4 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -389,6 +389,7 @@ extern BOOL show_systray; + extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; ++extern BOOL use_xfixes; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +@@ -396,6 +397,7 @@ extern int primary_monitor; + extern int copy_default_colors; + extern int alloc_system_colors; + extern int xrender_error_base; ++extern int xfixes_event_base; + extern HMODULE x11drv_module; + extern char *process_name; + extern Display *clipboard_display; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 9ec4c7a98f6..824237534d6 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -50,6 +50,7 @@ + + #include "x11drv.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/unicode.h" + #include "wine/debug.h" +@@ -69,6 +70,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_xkb = TRUE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; +@@ -87,6 +89,7 @@ int copy_default_colors = 128; + int alloc_system_colors = 256; + DWORD thread_data_tls_index = TLS_OUT_OF_INDEXES; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + HMODULE x11drv_module = 0; + char *process_name = NULL; + +@@ -501,6 +504,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -607,6 +667,9 @@ static BOOL process_attach(void) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.31.0 + +From 96a752ca89816d150c3d0bfb5a417df180ed94d4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:38:35 +0100 +Subject: [PATCH 10/10] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 797f8cfbbbf..91662103ca5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -102,6 +102,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1138,6 +1140,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1255,6 +1258,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index cdb3dd931e9..372ab1f6232 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -27,6 +27,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -232,10 +235,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index fd0e47233fd..686587f225c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -351,6 +351,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -373,7 +375,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + ExtEscape(surface->dc, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -405,8 +407,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 73926b23ab4..c56bdc9da75 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -390,6 +390,7 @@ extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; + extern BOOL use_xfixes; ++extern BOOL use_xpresent; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 824237534d6..823e25feaf4 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -51,6 +51,7 @@ + #include "x11drv.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/unicode.h" + #include "wine/debug.h" +@@ -71,6 +72,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_xkb = TRUE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; +@@ -511,6 +513,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -536,6 +539,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -561,6 +565,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -670,6 +725,9 @@ static BOOL process_attach(void) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.31.0 + diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-a6bc5f3.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-a6bc5f3.patch new file mode 100644 index 000000000..5031312e8 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-a6bc5f3.patch @@ -0,0 +1,2080 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,7 +333,7 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE, /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE, /* present the drawable on screen */ + X11DRV_FLUSH_GDI_DISPLAY /* flush the gdi display */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray DECLSPEC_HIDDEN; + extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; ++extern BOOL use_xfixes DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +@@ -433,6 +434,7 @@ extern int primary_monitor DECLSPEC_HIDDEN; + extern int copy_default_colors DECLSPEC_HIDDEN; + extern int alloc_system_colors DECLSPEC_HIDDEN; + extern int xrender_error_base DECLSPEC_HIDDEN; ++extern int xfixes_event_base DECLSPEC_HIDDEN; + extern char *process_name DECLSPEC_HIDDEN; + extern Display *clipboard_display DECLSPEC_HIDDEN; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; + extern BOOL use_xfixes DECLSPEC_HIDDEN; ++extern BOOL use_xpresent DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; +-extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( data->display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; ++extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) DECLSPEC_HIDDEN + extern void make_window_embedded( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; + extern Window create_dummy_client_window(void) DECLSPEC_HIDDEN; + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ) DECLSPEC_HIDDEN; ++extern void update_client_window( HWND hwnd ) DECLSPEC_HIDDEN; + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ) DECLSPEC_HIDDEN; + extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; + extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( data->display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c0042af.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c0042af.patch new file mode 100644 index 000000000..73d741eee --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c0042af.patch @@ -0,0 +1,2080 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,7 +333,7 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE, /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE, /* present the drawable on screen */ + X11DRV_FLUSH_GDI_DISPLAY /* flush the gdi display */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray; + extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; ++extern BOOL use_xfixes; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +@@ -433,6 +434,7 @@ extern int primary_monitor; + extern int copy_default_colors; + extern int alloc_system_colors; + extern int xrender_error_base; ++extern int xfixes_event_base; + extern char *process_name; + extern Display *clipboard_display; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; + extern BOOL use_xfixes; ++extern BOOL use_xpresent; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void); + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); +-extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); ++extern Window wine_vk_active_surface( HWND hwnd ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) + extern void make_window_embedded( struct x11drv_win_data *data ); + extern Window create_dummy_client_window(void); + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ); ++extern void update_client_window( HWND hwnd ); + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); + extern void change_systray_owner( Display *display, Window systray_window ); + extern HWND create_foreign_window( Display *display, Window window ); +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c14de4c.patch new file mode 100644 index 000000000..0c4cbacb9 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-c14de4c.patch @@ -0,0 +1,2080 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,7 +333,7 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE, /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE, /* present the drawable on screen */ + X11DRV_FLUSH_GDI_DISPLAY /* flush the gdi display */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray DECLSPEC_HIDDEN; + extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; ++extern BOOL use_xfixes DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +@@ -433,6 +434,7 @@ extern int primary_monitor DECLSPEC_HIDDEN; + extern int copy_default_colors DECLSPEC_HIDDEN; + extern int alloc_system_colors DECLSPEC_HIDDEN; + extern int xrender_error_base DECLSPEC_HIDDEN; ++extern int xfixes_event_base DECLSPEC_HIDDEN; + extern char *process_name DECLSPEC_HIDDEN; + extern Display *clipboard_display DECLSPEC_HIDDEN; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; + extern BOOL use_xfixes DECLSPEC_HIDDEN; ++extern BOOL use_xpresent DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; +-extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; ++extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) DECLSPEC_HIDDEN + extern void make_window_embedded( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; + extern Window create_dummy_client_window(void) DECLSPEC_HIDDEN; + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ) DECLSPEC_HIDDEN; ++extern void update_client_window( HWND hwnd ) DECLSPEC_HIDDEN; + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ) DECLSPEC_HIDDEN; + extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; + extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-a6bc5f3.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-a6bc5f3.patch new file mode 100644 index 000000000..4d6a6f409 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-a6bc5f3.patch @@ -0,0 +1,2079 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,6 +333,6 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE /* present the drawable on screen */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray DECLSPEC_HIDDEN; + extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; ++extern BOOL use_xfixes DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +@@ -433,6 +434,7 @@ extern int primary_monitor DECLSPEC_HIDDEN; + extern int copy_default_colors DECLSPEC_HIDDEN; + extern int alloc_system_colors DECLSPEC_HIDDEN; + extern int xrender_error_base DECLSPEC_HIDDEN; ++extern int xfixes_event_base DECLSPEC_HIDDEN; + extern char *process_name DECLSPEC_HIDDEN; + extern Display *clipboard_display DECLSPEC_HIDDEN; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; + extern BOOL use_xfixes DECLSPEC_HIDDEN; ++extern BOOL use_xpresent DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; +-extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( data->display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; ++extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) DECLSPEC_HIDDEN + extern void make_window_embedded( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; + extern Window create_dummy_client_window(void) DECLSPEC_HIDDEN; + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ) DECLSPEC_HIDDEN; ++extern void update_client_window( HWND hwnd ) DECLSPEC_HIDDEN; + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ) DECLSPEC_HIDDEN; + extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; + extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( data->display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c0042af.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c0042af.patch new file mode 100644 index 000000000..226f83612 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c0042af.patch @@ -0,0 +1,2079 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,6 +333,6 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE /* present the drawable on screen */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray; + extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; ++extern BOOL use_xfixes; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +@@ -433,6 +434,7 @@ extern int primary_monitor; + extern int copy_default_colors; + extern int alloc_system_colors; + extern int xrender_error_base; ++extern int xfixes_event_base; + extern char *process_name; + extern Display *clipboard_display; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer; + extern BOOL grab_fullscreen; + extern BOOL usexcomposite; + extern BOOL use_xfixes; ++extern BOOL use_xpresent; + extern BOOL managed_mode; + extern BOOL decorated_mode; + extern BOOL private_color_map; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void); + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ); + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); +-extern void wine_vk_surface_destroy( HWND hwnd ); ++extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void vulkan_thread_detach(void); + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ); + extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ); + extern void destroy_vk_surface( HWND hwnd ); + extern void sync_vk_surface( HWND hwnd, BOOL known_child ); + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ); ++extern Window wine_vk_active_surface( HWND hwnd ); + extern void vulkan_thread_detach(void); + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) + extern void make_window_embedded( struct x11drv_win_data *data ); + extern Window create_dummy_client_window(void); + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ); ++extern void update_client_window( HWND hwnd ); + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ); + extern void change_systray_owner( Display *display, Window systray_window ); + extern HWND create_foreign_window( Display *display, Window window ); +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c14de4c.patch new file mode 100644 index 000000000..3274f8605 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-c14de4c.patch @@ -0,0 +1,2079 @@ +From f7a98b358615e80a69257bc9706a9125056c0b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 12 Mar 2021 18:27:41 +0100 +Subject: [PATCH 01/14] winex11.drv: Store swapchain surfaces associations. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3e5c35ef570..743a964c44c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,6 +51,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + static pthread_mutex_t vulkan_mutex; + + static XContext vulkan_hwnd_context; ++static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 + +@@ -142,6 +143,8 @@ static void wine_vk_init(void) + #undef LOAD_OPTIONAL_FUNCPTR + + vulkan_hwnd_context = XUniqueContext(); ++ vulkan_swapchain_context = XUniqueContext(); ++ + return; + + fail: +@@ -287,6 +290,8 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + { + struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; ++ VkResult result; ++ + TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain); + + if (allocator) +@@ -298,7 +303,14 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- return pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); ++ if (result == VK_SUCCESS) ++ { ++ pthread_mutex_lock(&vulkan_mutex); ++ XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); ++ pthread_mutex_unlock(&vulkan_mutex); ++ } ++ return result; + } + + static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, +@@ -410,12 +422,20 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, + const VkAllocationCallbacks *allocator) + { ++ struct wine_vk_surface *surface; ++ + TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator); + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + pvkDestroySwapchainKHR(device, swapchain, NULL /* allocator */); ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ wine_vk_surface_release(surface); ++ XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, +-- +2.37.1 + +From 55fd68e3873c66dba228f66a5a62a63560bc1607 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 22 Apr 2021 23:38:16 +0200 +Subject: [PATCH 02/14] winex11.drv: Hook vkAcquireNextImage(2)KHR functions. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +--- + dlls/winemac.drv/vulkan.c | 2 ++ + dlls/winevulkan/make_vulkan | 2 ++ + dlls/winex11.drv/vulkan.c | 19 +++++++++++++++++++ + include/wine/vulkan_driver.h | 6 ++++++ + 4 files changed, 29 insertions(+) + +diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c +index 00f5e8465ab..0c1a11af63d 100644 +--- a/dlls/winemac.drv/vulkan.c ++++ b/dlls/winemac.drv/vulkan.c +@@ -591,6 +591,8 @@ static VkSurfaceKHR macdrv_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ NULL, ++ NULL, + macdrv_vkCreateInstance, + macdrv_vkCreateSwapchainKHR, + macdrv_vkCreateWin32SurfaceKHR, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 2eeff42c56e..d905412ca0d 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -226,6 +226,8 @@ FUNCTION_OVERRIDES = { + "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + + # VK_KHR_swapchain ++ "vkAcquireNextImageKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, ++ "vkAcquireNextImage2KHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 743a964c44c..71ab92297d2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -76,6 +76,7 @@ typedef struct VkXlibSurfaceCreateInfoKHR + Window window; + } VkXlibSurfaceCreateInfoKHR; + ++static VkResult (*pvkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + static VkResult (*pvkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + static VkResult (*pvkCreateXlibSurfaceKHR)(VkInstance, const VkXlibSurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -119,6 +120,7 @@ static void wine_vk_init(void) + + #define LOAD_FUNCPTR(f) if (!(p##f = dlsym(vulkan_handle, #f))) goto fail + #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(vulkan_handle, #f) ++ LOAD_FUNCPTR(vkAcquireNextImageKHR); + LOAD_FUNCPTR(vkCreateInstance); + LOAD_FUNCPTR(vkCreateSwapchainKHR); + LOAD_FUNCPTR(vkCreateXlibSurfaceKHR); +@@ -284,6 +286,21 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, + return res; + } + ++static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, ++ VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, ++ VkFence fence, uint32_t *image_index) ++{ ++ return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++} ++ ++static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, ++ const VkAcquireNextImageInfoKHR *acquire_info, uint32_t *image_index) ++{ ++ static int once; ++ if (!once++) FIXME("Emulating vkGetPhysicalDeviceSurfaceCapabilities2KHR with vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pNext is ignored.\n"); ++ return X11DRV_vkAcquireNextImageKHR(device, acquire_info->swapchain, acquire_info->timeout, acquire_info->semaphore, acquire_info->fence, image_index); ++} ++ + static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) +@@ -708,6 +725,8 @@ static VkSurfaceKHR X11DRV_wine_get_native_surface(VkSurfaceKHR surface) + + static const struct vulkan_funcs vulkan_funcs = + { ++ X11DRV_vkAcquireNextImage2KHR, ++ X11DRV_vkAcquireNextImageKHR, + X11DRV_vkCreateInstance, + X11DRV_vkCreateSwapchainKHR, + X11DRV_vkCreateWin32SurfaceKHR, +diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h +index f5269d554fb..39a9a361736 100644 +--- a/include/wine/vulkan_driver.h ++++ b/include/wine/vulkan_driver.h +@@ -21,6 +21,8 @@ struct vulkan_funcs + * needs to provide. Other function calls will be provided indirectly by dispatch + * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. + */ ++ VkResult (*p_vkAcquireNextImage2KHR)(VkDevice, const VkAcquireNextImageInfoKHR *, uint32_t *); ++ VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *); + VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); + VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *); + VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *); +@@ -55,6 +57,10 @@ static inline void *get_vulkan_driver_device_proc_addr( + + name += 2; + ++ if (!strcmp(name, "AcquireNextImage2KHR")) ++ return vulkan_funcs->p_vkAcquireNextImage2KHR; ++ if (!strcmp(name, "AcquireNextImageKHR")) ++ return vulkan_funcs->p_vkAcquireNextImageKHR; + if (!strcmp(name, "CreateSwapchainKHR")) + return vulkan_funcs->p_vkCreateSwapchainKHR; + if (!strcmp(name, "DestroySwapchainKHR")) +-- +2.37.1 + +From 9d83c4c95baab64dc432421c6c0f050564cb9ff1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:23:49 +0200 +Subject: [PATCH 03/14] winex11.drv: Rename X11DRV_FLUSH_GL_DRAWABLE to + X11DRV_PRESENT_DRAWABLE. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 ++++---- + dlls/winex11.drv/opengl.c | 40 +++++++++++++++++++-------------------- + dlls/winex11.drv/x11drv.h | 8 ++++---- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 7c5a1acd7b6..cdaa4746758 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -244,16 +244,16 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + return TRUE; + } + break; +- case X11DRV_FLUSH_GL_DRAWABLE: +- if (in_count >= sizeof(struct x11drv_escape_flush_gl_drawable)) ++ case X11DRV_PRESENT_DRAWABLE: ++ if (in_count >= sizeof(struct x11drv_escape_present_drawable)) + { +- const struct x11drv_escape_flush_gl_drawable *data = in_data; ++ const struct x11drv_escape_present_drawable *data = in_data; + RECT rect = physDev->dc_rect; + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); + XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->gl_drawable, physDev->drawable, physDev->gc, ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, + 0, 0, rect.right, rect.bottom, + physDev->dc_rect.left, physDev->dc_rect.top ); + add_device_bounds( physDev, &rect ); +diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c +index 7ceaeb7c2df..104d922c156 100644 +--- a/dlls/winex11.drv/opengl.c ++++ b/dlls/winex11.drv/opengl.c +@@ -1955,20 +1955,20 @@ static BOOL WINAPI glxdrv_wglShareLists(struct wgl_context *org, struct wgl_cont + + static void wglFinish(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -1976,26 +1976,26 @@ static void wglFinish(void) + } + + pglFinish(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + + static void wglFlush(void) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = FALSE; + + if ((gl = get_gl_drawable( NtUserWindowFromDC( ctx->hdc ), 0 ))) + { + switch (gl->type) + { +- case DC_GL_PIXMAP_WIN: escape.gl_drawable = gl->pixmap; break; +- case DC_GL_CHILD_WIN: escape.gl_drawable = gl->window; break; ++ case DC_GL_PIXMAP_WIN: escape.drawable = gl->pixmap; break; ++ case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + default: break; + } + sync_context(ctx); +@@ -2003,7 +2003,7 @@ static void wglFlush(void) + } + + pglFlush(); +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + } + +@@ -3321,15 +3321,15 @@ static void X11DRV_WineGL_LoadExtensions(void) + */ + static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { +- struct x11drv_escape_flush_gl_drawable escape; ++ struct x11drv_escape_present_drawable escape; + struct gl_drawable *gl; + struct wgl_context *ctx = NtCurrentTeb()->glContext; + INT64 ust, msc, sbc, target_sbc = 0; + + TRACE("(%p)\n", hdc); + +- escape.code = X11DRV_FLUSH_GL_DRAWABLE; +- escape.gl_drawable = 0; ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = 0; + escape.flush = !pglXWaitForSbcOML; + + if (!(gl = get_gl_drawable( NtUserWindowFromDC( hdc ), hdc ))) +@@ -3350,7 +3350,7 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + { + case DC_GL_PIXMAP_WIN: + if (ctx) sync_context( ctx ); +- escape.gl_drawable = gl->pixmap; ++ escape.drawable = gl->pixmap; + if (pglXCopySubBufferMESA) { + /* (glX)SwapBuffers has an implicit glFlush effect, however + * GLX_MESA_copy_sub_buffer doesn't. Make sure GL is flushed before +@@ -3371,10 +3371,10 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + case DC_GL_WINDOW: + case DC_GL_CHILD_WIN: + if (ctx) sync_context( ctx ); +- if (gl->type == DC_GL_CHILD_WIN) escape.gl_drawable = gl->window; ++ if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + /* fall through */ + default: +- if (escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); +@@ -3384,12 +3384,12 @@ static BOOL WINAPI glxdrv_wglSwapBuffers( HDC hdc ) + break; + } + +- if (escape.gl_drawable && pglXWaitForSbcOML) ++ if (escape.drawable && pglXWaitForSbcOML) + pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); + + release_gl_drawable( gl ); + +- if (escape.gl_drawable) ++ if (escape.drawable) + NtGdiExtEscape( ctx->hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); + return TRUE; + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index e124ca57bad..537b8d12704 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -333,6 +333,6 @@ enum x11drv_escape_codes + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ +- X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ ++ X11DRV_PRESENT_DRAWABLE /* present the drawable on screen */ + }; + +@@ -352,10 +352,10 @@ struct x11drv_escape_get_drawable + int pixel_format; /* internal GL pixel format */ + }; + +-struct x11drv_escape_flush_gl_drawable ++struct x11drv_escape_present_drawable + { +- enum x11drv_escape_codes code; /* escape code (X11DRV_FLUSH_GL_DRAWABLE) */ +- Drawable gl_drawable; /* GL drawable */ ++ enum x11drv_escape_codes code; /* escape code (X11DRV_PRESENT_DRAWABLE) */ ++ Drawable drawable; /* GL / VK drawable */ + BOOL flush; /* flush X11 before copying */ + }; + +-- +2.37.1 + +From 06706f2eff9769ea16e7f1f28dba680d22bc2174 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sun, 17 Apr 2022 22:26:59 +0200 +Subject: [PATCH 04/14] winex11.drv: Support child window vulkan rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 91 +++++++++++++++++++++++++++++++++++---- + dlls/winex11.drv/window.c | 17 ++++++++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 100 insertions(+), 9 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 71ab92297d2..210c7989b3c 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -36,6 +36,7 @@ + #include "wine/debug.h" + #include "wine/prof.h" + #include "x11drv.h" ++#include "xcomposite.h" + + #define VK_NO_PROTOTYPES + #define WINE_VK_HOST +@@ -63,6 +64,8 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ BOOL offscreen; /* drawable is offscreen */ ++ HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; + }; +@@ -229,15 +232,54 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + void wine_vk_surface_destroy(HWND hwnd) + { + struct wine_vk_surface *surface; ++ HDC hdc = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) + { ++ hdc = surface->hdc; + surface->hwnd_thread_id = 0; +- surface->hwnd = NULL; ++ surface->hwnd = 0; ++ surface->hdc = 0; + wine_vk_surface_release(surface); + } + XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); ++ if (hdc) NtUserReleaseDC(hwnd, hdc); ++} ++ ++static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) ++{ ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) ++ { ++ if (!surface->offscreen && offscreen) ++ { ++ FIXME("Redirecting vulkan surface offscreen, expect degraded performance.\n"); ++ pXCompositeRedirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ else if (surface->offscreen && !offscreen) ++ { ++ FIXME("Putting vulkan surface back onscreen, expect standard performance.\n"); ++ pXCompositeUnredirectWindow(gdi_display, surface->window, CompositeRedirectManual); ++ } ++ surface->offscreen = offscreen; ++ return TRUE; ++ } ++#endif ++ ++ if (offscreen) FIXME("Application requires child window rendering, which is not implemented yet!\n"); ++ surface->offscreen = offscreen; ++ return !offscreen; ++} ++ ++void sync_vk_surface(HWND hwnd, BOOL known_child) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ wine_vk_surface_set_offscreen(surface, known_child); ++ pthread_mutex_unlock(&vulkan_mutex); + } + + void vulkan_thread_detach(void) +@@ -290,7 +332,30 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { +- return pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ struct x11drv_escape_present_drawable escape; ++ struct wine_vk_surface *surface = NULL; ++ VkResult result; ++ HDC hdc = 0; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ wine_vk_surface_grab(surface); ++ hdc = surface->hdc; ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); ++ if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ { ++ escape.code = X11DRV_PRESENT_DRAWABLE; ++ escape.drawable = surface->window; ++ escape.flush = TRUE; ++ NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ } ++ ++ if (surface) wine_vk_surface_release(surface); ++ return result; + } + + static VkResult X11DRV_vkAcquireNextImage2KHR(VkDevice device, +@@ -343,13 +408,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + +- /* TODO: support child window rendering. */ +- if (create_info->hwnd && NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow()) +- { +- FIXME("Application requires child window rendering, which is not implemented yet!\n"); +- return VK_ERROR_INCOMPATIBLE_DRIVER; +- } +- + x11_surface = calloc(1, sizeof(*x11_surface)); + if (!x11_surface) + return VK_ERROR_OUT_OF_HOST_MEMORY; +@@ -358,6 +416,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->hwnd = create_info->hwnd; + if (x11_surface->hwnd) + { ++ x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); + x11_surface->window = create_client_window(create_info->hwnd, &default_visual); + x11_surface->hwnd_thread_id = NtUserGetWindowThread(x11_surface->hwnd, NULL); + } +@@ -375,6 +434,16 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + goto err; + } + ++ if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || ++ NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) ++ { ++ if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) ++ { ++ res = VK_ERROR_INCOMPATIBLE_DRIVER; ++ goto err; ++ } ++ } ++ + create_info_host.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info_host.pNext = NULL; + create_info_host.flags = 0; /* reserved */ +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 34596a7d7d7..6fbf938a477 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1736,6 +1736,16 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + { + struct x11drv_win_data *data; + DWORD changed = style->styleNew ^ style->styleOld; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (offset == GWL_STYLE && (changed & WS_CHILD)) ++ { ++ if (NtUserGetWindowRelative( parent, GW_CHILD ) || ++ NtUserGetAncestor( parent, GA_PARENT ) != NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, TRUE ); ++ else ++ sync_vk_surface( parent, FALSE ); ++ } + + if (hwnd == NtUserGetDesktopWindow()) return; + if (!(data = get_win_data( hwnd ))) return; +@@ -1762,6 +1772,11 @@ void X11DRV_DestroyWindow( HWND hwnd ) + { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + struct x11drv_win_data *data; ++ HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); ++ ++ if (!NtUserGetWindowRelative( parent, GW_CHILD ) && ++ NtUserGetAncestor( parent, GA_PARENT ) == NtUserGetDesktopWindow()) ++ sync_vk_surface( parent, FALSE ); + + if (!(data = get_win_data( hwnd ))) return; + +@@ -1972,6 +1987,7 @@ static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *wi + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + display = thread_init_display(); + init_clip_window(); /* make sure the clip window is initialized in this thread */ +@@ -2450,6 +2466,7 @@ done: + * that will need clipping support. + */ + sync_gl_drawable( parent, TRUE ); ++ sync_vk_surface( parent, TRUE ); + + fetch_icon_data( hwnd, 0, 0 ); + } +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 537b8d12704..2703322c981 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -637,6 +637,7 @@ extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 9aacdc4a7160c683df85709daf156f32fa04a5d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:00:23 +0200 +Subject: [PATCH 05/14] winex11.drv: Wait on vkAcquireNextImageKHR before + flushing. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To prevent tearing, when present mode is mailbox or fifo. + +Based on a patch from Felix Hädicke . +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 210c7989b3c..34b1be5c61b 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + struct list entry; + Window window; + VkSurfaceKHR surface; /* native surface */ ++ VkPresentModeKHR present_mode; + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -100,6 +101,9 @@ static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint3 + static VkBool32 (*pvkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice, uint32_t, Display *, VisualID); + static VkResult (*pvkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *); + static VkResult (*pvkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *); ++static VkResult (*pvkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout); ++static VkResult (*pvkCreateFence)(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); ++static void (*pvkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator); + + static void *X11DRV_get_vk_device_proc_addr(const char *name); + static void *X11DRV_get_vk_instance_proc_addr(VkInstance instance, const char *name); +@@ -144,6 +148,9 @@ static void wine_vk_init(void) + LOAD_FUNCPTR(vkQueuePresentKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetDeviceGroupSurfacePresentModesKHR); + LOAD_OPTIONAL_FUNCPTR(vkGetPhysicalDevicePresentRectanglesKHR); ++ LOAD_FUNCPTR(vkWaitForFences); ++ LOAD_FUNCPTR(vkCreateFence); ++ LOAD_FUNCPTR(vkDestroyFence); + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +@@ -332,9 +339,12 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, + VkFence fence, uint32_t *image_index) + { ++ static int once; + struct x11drv_escape_present_drawable escape; + struct wine_vk_surface *surface = NULL; + VkResult result; ++ VkFence orig_fence; ++ BOOL wait_fence = FALSE; + HDC hdc = 0; + + pthread_mutex_lock(&vulkan_mutex); +@@ -345,15 +355,35 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + pthread_mutex_unlock(&vulkan_mutex); + ++ if (!surface || !surface->offscreen) ++ wait_fence = FALSE; ++ else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || ++ surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) ++ wait_fence = TRUE; ++ ++ orig_fence = fence; ++ if (wait_fence && !fence) ++ { ++ VkFenceCreateInfo create_info; ++ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; ++ create_info.pNext = NULL; ++ create_info.flags = 0; ++ pvkCreateFence(device, &create_info, NULL, &fence); ++ } ++ + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); + if (result == VK_SUCCESS && hdc && surface && surface->offscreen) + { ++ if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); ++ if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + ++ if (fence != orig_fence) pvkDestroyFence(device, fence, NULL); + if (surface) wine_vk_surface_release(surface); + return result; + } +@@ -385,6 +415,11 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + ++ /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; ++ x11_surface->present_mode = create_info->presentMode; ++ + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +-- +2.37.1 + +From 0e4d287b343217eea3acdd7c7cc64d05ee2622ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 11 Mar 2021 23:27:57 +0100 +Subject: [PATCH 06/14] winex11.drv: Remove unused X11DRV_GET_DRAWABLE + ExtEscape code. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/init.c | 8 -------- + dlls/winex11.drv/x11drv.h | 9 --------- + 2 files changed, 17 deletions(-) + +-- +2.37.1 + +From 36348903074efbb71c0be364bbdbe8ceee04440c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:16 +0200 +Subject: [PATCH 07/14] winex11.drv: Move Xfixes extension query to + process_attach. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/clipboard.c | 26 ++------------ + dlls/winex11.drv/x11drv.h | 2 ++ + dlls/winex11.drv/x11drv_main.c | 63 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 36 +++++++++++++++++++ + 4 files changed, 103 insertions(+), 24 deletions(-) + create mode 100644 dlls/winex11.drv/xfixes.h + +diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c +index da451fad57c..69eb75e8822 100644 +--- a/dlls/winex11.drv/clipboard.c ++++ b/dlls/winex11.drv/clipboard.c +@@ -83,6 +83,7 @@ + #include "ntstatus.h" + #define WIN32_NO_STATUS + #include "x11drv.h" ++#include "xfixes.h" + + #ifdef HAVE_X11_EXTENSIONS_XFIXES_H + #include +@@ -199,7 +200,6 @@ static UINT rendered_formats; + static ULONG last_clipboard_update; + static struct clipboard_format **current_x11_formats; + static unsigned int nb_current_x11_formats; +-static BOOL use_xfixes; + + Display *clipboard_display = NULL; + +@@ -2170,28 +2170,6 @@ static BOOL selection_notify_event( HWND hwnd, XEvent *event ) + static void xfixes_init(void) + { + #ifdef SONAME_LIBXFIXES +- typeof(XFixesSelectSelectionInput) *pXFixesSelectSelectionInput; +- typeof(XFixesQueryExtension) *pXFixesQueryExtension; +- typeof(XFixesQueryVersion) *pXFixesQueryVersion; +- +- int event_base, error_base; +- int major = 3, minor = 0; +- void *handle; +- +- handle = dlopen(SONAME_LIBXFIXES, RTLD_NOW); +- if (!handle) return; +- +- pXFixesQueryExtension = dlsym(handle, "XFixesQueryExtension"); +- if (!pXFixesQueryExtension) return; +- pXFixesQueryVersion = dlsym(handle, "XFixesQueryVersion"); +- if (!pXFixesQueryVersion) return; +- pXFixesSelectSelectionInput = dlsym(handle, "XFixesSelectSelectionInput"); +- if (!pXFixesSelectSelectionInput) return; +- +- if (!pXFixesQueryExtension(clipboard_display, &event_base, &error_base)) +- return; +- pXFixesQueryVersion(clipboard_display, &major, &minor); +- use_xfixes = (major >= 1); + if (!use_xfixes) return; + + pXFixesSelectSelectionInput(clipboard_display, import_window, x11drv_atom(CLIPBOARD), +@@ -2205,7 +2183,7 @@ static void xfixes_init(void) + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask); + } +- X11DRV_register_event_handler(event_base + XFixesSelectionNotify, ++ X11DRV_register_event_handler(xfixes_event_base + XFixesSelectionNotify, + selection_notify_event, "XFixesSelectionNotify"); + TRACE("xfixes succesully initialized\n"); + #else +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 890770c5248..830bf11ae4c 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -426,6 +426,7 @@ extern BOOL show_systray DECLSPEC_HIDDEN; + extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; ++extern BOOL use_xfixes DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +@@ -433,6 +434,7 @@ extern int primary_monitor DECLSPEC_HIDDEN; + extern int copy_default_colors DECLSPEC_HIDDEN; + extern int alloc_system_colors DECLSPEC_HIDDEN; + extern int xrender_error_base DECLSPEC_HIDDEN; ++extern int xfixes_event_base DECLSPEC_HIDDEN; + extern char *process_name DECLSPEC_HIDDEN; + extern Display *clipboard_display DECLSPEC_HIDDEN; + extern WNDPROC client_foreign_window_proc; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 20e07679635..461a9d6fe6e 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -52,6 +52,7 @@ + #include "x11drv.h" + #include "winreg.h" + #include "xcomposite.h" ++#include "xfixes.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -72,6 +73,7 @@ Window root_window; + BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; ++BOOL use_xfixes = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -89,6 +91,7 @@ BOOL shape_layered_windows = TRUE; + int copy_default_colors = 128; + int alloc_system_colors = 256; + int xrender_error_base = 0; ++int xfixes_event_base = 0; + char *process_name = NULL; + WNDPROC client_foreign_window_proc = NULL; + +@@ -596,6 +599,63 @@ sym_not_found: + } + #endif /* defined(SONAME_LIBXCOMPOSITE) */ + ++#ifdef SONAME_LIBXFIXES ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xfixes(void) ++{ ++ int event, error, major = 3, minor = 0; ++ void *xfixes; ++ ++ if (!(xfixes = dlopen(SONAME_LIBXFIXES, RTLD_NOW))) ++ { ++ WARN("Xfixes library %s not found, disabled.\n", SONAME_LIBXFIXES); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym(xfixes, #f))) \ ++ { \ ++ WARN("Xfixes function %s not found, disabled\n", #f); \ ++ dlclose(xfixes); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XFixesQueryExtension) ++ LOAD_FUNCPTR(XFixesQueryVersion) ++ LOAD_FUNCPTR(XFixesCreateRegion) ++ LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesSelectSelectionInput) ++#undef LOAD_FUNCPTR ++ ++ if (!pXFixesQueryExtension(gdi_display, &event, &error)) ++ { ++ WARN("Xfixes extension not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ if (!pXFixesQueryVersion(gdi_display, &major, &minor) || ++ major < 2) ++ { ++ WARN("Xfixes version 2.0 not found, disabled.\n"); ++ dlclose(xfixes); ++ return; ++ } ++ ++ TRACE("Xfixes, error %d, event %d, version %d.%d found\n", ++ error, event, major, minor); ++ use_xfixes = TRUE; ++ xfixes_event_base = event; ++} ++#endif /* SONAME_LIBXFIXES */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -701,6 +761,9 @@ static NTSTATUS x11drv_init( void *arg ) + X11DRV_XF86VM_Init(); + /* initialize XRandR */ + X11DRV_XRandR_Init(); ++#ifdef SONAME_LIBXFIXES ++ x11drv_load_xfixes(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +new file mode 100644 +index 00000000000..3ab31201d3d +--- /dev/null ++++ b/dlls/winex11.drv/xfixes.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xfixes interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XFIXES_H ++#define __WINE_XFIXES_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXFIXES ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XFixesQueryExtension) ++MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesSelectSelectionInput) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXFIXES) */ ++ ++#endif /* __WINE_XFIXES_H */ +-- +2.37.1 + +From 3403bd6f3c0179698b69c5155139a6b3a443b25f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:01:50 +0200 +Subject: [PATCH 08/14] winex11.drv: Use XPresentPixmap instead of XCopyArea + when available. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + configure.ac | 13 ++++++++ + dlls/winex11.drv/init.c | 34 +++++++++++++++++--- + dlls/winex11.drv/vulkan.c | 9 ++++-- + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/x11drv_main.c | 58 ++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/xfixes.h | 3 ++ + dlls/winex11.drv/xpresent.h | 36 +++++++++++++++++++++ + 7 files changed, 147 insertions(+), 7 deletions(-) + create mode 100644 dlls/winex11.drv/xpresent.h + +diff --git a/configure.ac b/configure.ac +index 554c1930968..6ba9669dcc2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -76,6 +76,8 @@ AC_ARG_WITH(xinput, AS_HELP_STRING([--without-xinput],[do not use the Xinput + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput_h=no; fi]) + AC_ARG_WITH(xinput2, AS_HELP_STRING([--without-xinput2],[do not use the Xinput 2 extension]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_XInput2_h=no; fi]) ++AC_ARG_WITH(xpresent, AS_HELP_STRING([--without-xpresent],[do not use the Xpresent extension]), ++ [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xpresent_h=no; fi]) + AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[do not use Xrandr (multi-monitor support)]), + [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xrandr_h=no; fi]) + AC_ARG_WITH(xrender, AS_HELP_STRING([--without-xrender],[do not use the Xrender extension]), +@@ -1183,6 +1185,7 @@ then + X11/extensions/Xcomposite.h \ + X11/extensions/Xfixes.h \ + X11/extensions/Xinerama.h \ ++ X11/extensions/Xpresent.h \ + X11/extensions/Xrandr.h \ + X11/extensions/Xrender.h \ + X11/extensions/xf86vmode.h \ +@@ -1300,6 +1303,16 @@ then + WINE_NOTICE_WITH(xinerama,[test "x$ac_cv_lib_soname_Xinerama" = "x"], + [libxinerama ${notice_platform}development files not found, multi-monitor setups won't be supported.]) + ++ dnl *** Check for Xpresent extension ++ if test "$ac_cv_header_X11_extensions_Xpresent_h" = "yes" ++ then ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ++#include ]], [[static typeof(XPresentQueryVersion) * func; if (func) return 0;]])], ++ [WINE_CHECK_SONAME(Xpresent,XPresentQueryVersion,,,[$X_LIBS $X_EXTRA_LIBS])]) ++ fi ++ WINE_NOTICE_WITH(Xpresent,[test "x$ac_cv_lib_soname_Xpresent" = "x"], ++ [libXpresent ${notice_platform}development files not found, Xpresent won't be supported.]) ++ + dnl *** Check for X Composite extension + if test "$ac_cv_header_X11_extensions_Xcomposite_h" = "yes" + then +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index 5696db26604..dff2332d247 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -31,6 +31,9 @@ + #include "winbase.h" + #include "winreg.h" + #include "x11drv.h" ++#include "xfixes.h" ++#include "xpresent.h" ++#include "xcomposite.h" + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +@@ -244,10 +247,33 @@ static INT CDECL X11DRV_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOI + + OffsetRect( &rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); + if (data->flush) XFlush( gdi_display ); +- XSetFunction( gdi_display, physDev->gc, GXcopy ); +- XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, +- 0, 0, rect.right, rect.bottom, +- physDev->dc_rect.left, physDev->dc_rect.top ); ++ ++#if defined(SONAME_LIBXPRESENT) && defined(SONAME_LIBXFIXES) ++ if (use_xpresent && use_xfixes && usexcomposite) ++ { ++ XserverRegion update, valid; ++ XRectangle xrect = {0, 0, rect.right - rect.left, rect.bottom - rect.top}; ++ Drawable drawable = data->drawable; ++ update = pXFixesCreateRegionFromGC( gdi_display, physDev->gc ); ++ valid = pXFixesCreateRegion( gdi_display, &xrect, 1 ); ++#ifdef SONAME_LIBXCOMPOSITE ++ if (usexcomposite) drawable = pXCompositeNameWindowPixmap( gdi_display, drawable ); ++#endif ++ pXPresentPixmap( gdi_display, physDev->drawable, drawable, XNextRequest( gdi_display ), ++ valid, update, physDev->dc_rect.left, physDev->dc_rect.top, None, None, ++ None, 0, 0, 0, 0, NULL, 0 ); ++ pXFixesDestroyRegion( gdi_display, update ); ++ pXFixesDestroyRegion( gdi_display, valid ); ++ } ++ else ++#endif ++ { ++ XSetFunction( gdi_display, physDev->gc, GXcopy ); ++ XCopyArea( gdi_display, data->drawable, physDev->drawable, physDev->gc, ++ 0, 0, rect.right, rect.bottom, ++ physDev->dc_rect.left, physDev->dc_rect.top ); ++ } ++ + add_device_bounds( physDev, &rect ); + return TRUE; + } +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 34b1be5c61b..7b1e25163c7 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -357,6 +357,8 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + + if (!surface || !surface->offscreen) + wait_fence = FALSE; ++ else if (use_xpresent && use_xfixes && usexcomposite) /* X11DRV_PRESENT_DRAWABLE will use XPresentPixmap */ ++ wait_fence = FALSE; + else if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR || + surface->present_mode == VK_PRESENT_MODE_FIFO_KHR) + wait_fence = TRUE; +@@ -379,7 +381,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + escape.drawable = surface->window; + escape.flush = TRUE; + NtGdiExtEscape(hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (char *)&escape, 0, NULL); +- if (surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ++ if (wait_fence && surface->present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + if (once++) FIXME("Application requires child window rendering with mailbox present mode, expect possible tearing!\n"); + } + +@@ -415,8 +417,9 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host = *create_info; + create_info_host.surface = x11_surface->surface; + +- /* force fifo when running offscreen so the acquire fence is more likely to be vsynced */ +- if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) ++ /* unless we use XPresentPixmap, force fifo when running offscreen so the acquire fence is more likely to be vsynced */ ++ if (x11_surface->offscreen && create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR && ++ !(use_xpresent && use_xfixes && usexcomposite)) + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 830bf11ae4c..eb38909a47d 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -427,6 +427,7 @@ extern BOOL grab_pointer DECLSPEC_HIDDEN; + extern BOOL grab_fullscreen DECLSPEC_HIDDEN; + extern BOOL usexcomposite DECLSPEC_HIDDEN; + extern BOOL use_xfixes DECLSPEC_HIDDEN; ++extern BOOL use_xpresent DECLSPEC_HIDDEN; + extern BOOL managed_mode DECLSPEC_HIDDEN; + extern BOOL decorated_mode DECLSPEC_HIDDEN; + extern BOOL private_color_map DECLSPEC_HIDDEN; +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 461a9d6fe6e..92897c16238 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -53,6 +53,7 @@ + #include "winreg.h" + #include "xcomposite.h" + #include "xfixes.h" ++#include "xpresent.h" + #include "wine/server.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -74,6 +75,7 @@ BOOL usexvidmode = TRUE; + BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; ++BOOL use_xpresent = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +@@ -606,6 +608,7 @@ MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) + MAKE_FUNCPTR(XFixesCreateRegion) + MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + +@@ -631,6 +634,7 @@ static void x11drv_load_xfixes(void) + LOAD_FUNCPTR(XFixesQueryVersion) + LOAD_FUNCPTR(XFixesCreateRegion) + LOAD_FUNCPTR(XFixesCreateRegionFromGC) ++ LOAD_FUNCPTR(XFixesDestroyRegion) + LOAD_FUNCPTR(XFixesSelectSelectionInput) + #undef LOAD_FUNCPTR + +@@ -656,6 +660,57 @@ static void x11drv_load_xfixes(void) + } + #endif /* SONAME_LIBXFIXES */ + ++#ifdef SONAME_LIBXPRESENT ++ ++#define MAKE_FUNCPTR(f) typeof(f) * p##f; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++ ++static void x11drv_load_xpresent(void) ++{ ++ int opcode, event, error, major = 1, minor = 0; ++ void *xpresent; ++ ++ if (!(xpresent = dlopen( SONAME_LIBXPRESENT, RTLD_NOW ))) ++ { ++ WARN( "Xpresent library %s not found, disabled.\n", SONAME_LIBXPRESENT ); ++ return; ++ } ++ ++#define LOAD_FUNCPTR(f) \ ++ if (!(p##f = dlsym( xpresent, #f ))) \ ++ { \ ++ WARN( "Xpresent function %s not found, disabled\n", #f ); \ ++ dlclose( xpresent ); \ ++ return; \ ++ } ++ LOAD_FUNCPTR(XPresentQueryExtension) ++ LOAD_FUNCPTR(XPresentQueryVersion) ++ LOAD_FUNCPTR(XPresentPixmap) ++#undef LOAD_FUNCPTR ++ ++ if (!pXPresentQueryExtension( gdi_display, &opcode, &event, &error )) ++ { ++ WARN("Xpresent extension not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ if (!pXPresentQueryVersion( gdi_display, &major, &minor )) ++ { ++ WARN("Xpresent version not found, disabled.\n"); ++ dlclose(xpresent); ++ return; ++ } ++ ++ TRACE( "Xpresent, opcode %d, error %d, event %d, version %d.%d found\n", ++ opcode, error, event, major, minor ); ++ use_xpresent = TRUE; ++} ++#endif /* SONAME_LIBXPRESENT */ ++ + static void init_visuals( Display *display, int screen ) + { + int count; +@@ -764,6 +819,9 @@ static NTSTATUS x11drv_init( void *arg ) + #ifdef SONAME_LIBXFIXES + x11drv_load_xfixes(); + #endif ++#ifdef SONAME_LIBXPRESENT ++ x11drv_load_xpresent(); ++#endif + #ifdef SONAME_LIBXCOMPOSITE + X11DRV_XComposite_Init(); + #endif +diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h +index 3ab31201d3d..80f8cca04e4 100644 +--- a/dlls/winex11.drv/xfixes.h ++++ b/dlls/winex11.drv/xfixes.h +@@ -29,6 +29,9 @@ + #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; + MAKE_FUNCPTR(XFixesQueryExtension) + MAKE_FUNCPTR(XFixesQueryVersion) ++MAKE_FUNCPTR(XFixesCreateRegion) ++MAKE_FUNCPTR(XFixesCreateRegionFromGC) ++MAKE_FUNCPTR(XFixesDestroyRegion) + MAKE_FUNCPTR(XFixesSelectSelectionInput) + #undef MAKE_FUNCPTR + #endif /* defined(SONAME_LIBXFIXES) */ +diff --git a/dlls/winex11.drv/xpresent.h b/dlls/winex11.drv/xpresent.h +new file mode 100644 +index 00000000000..6fd813a930e +--- /dev/null ++++ b/dlls/winex11.drv/xpresent.h +@@ -0,0 +1,36 @@ ++/* ++ * Wine X11DRV Xpresent interface ++ * ++ * Copyright 2021 RĂ©mi Bernon 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 ++ */ ++#ifndef __WINE_XPRESENT_H ++#define __WINE_XPRESENT_H ++ ++#ifndef __WINE_CONFIG_H ++# error You must include config.h to use this header ++#endif ++ ++#ifdef SONAME_LIBXPRESENT ++#include ++#define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; ++MAKE_FUNCPTR(XPresentQueryExtension) ++MAKE_FUNCPTR(XPresentQueryVersion) ++MAKE_FUNCPTR(XPresentPixmap) ++#undef MAKE_FUNCPTR ++#endif /* defined(SONAME_LIBXPRESENT) */ ++ ++#endif /* __WINE_XPRESENT_H */ +-- +2.37.1 + +From 6d9402faf0d0c561594941b7b728312ae930d1f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Sat, 7 May 2022 01:02:53 +0200 +Subject: [PATCH 09/14] winex11.drv: Support multiple vulkan surfaces per HWND. + +Fixes games failing to render after displaying a video, e.g. Age of +Empires II (2013). + + https://github.com/doitsujin/dxvk/issues/1726 +--- + dlls/winex11.drv/vulkan.c | 51 ++++++++++++++++++++------------------- + dlls/winex11.drv/window.c | 2 +- + dlls/winex11.drv/x11drv.h | 2 +- + 3 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 7b1e25163c7..d3e554090c2 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -51,7 +51,6 @@ WINE_DECLARE_DEBUG_CHANNEL(fps); + + static pthread_mutex_t vulkan_mutex; + +-static XContext vulkan_hwnd_context; + static XContext vulkan_swapchain_context; + + #define VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR 1000004000 +@@ -154,7 +153,6 @@ static void wine_vk_init(void) + #undef LOAD_FUNCPTR + #undef LOAD_OPTIONAL_FUNCPTR + +- vulkan_hwnd_context = XUniqueContext(); + vulkan_swapchain_context = XUniqueContext(); + + return; +@@ -236,23 +234,30 @@ static void wine_vk_surface_release(struct wine_vk_surface *surface) + free(surface); + } + +-void wine_vk_surface_destroy(HWND hwnd) ++void wine_vk_surface_destroy(struct wine_vk_surface *surface) + { +- struct wine_vk_surface *surface; +- HDC hdc = 0; ++ TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); ++ XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); ++ XSync(gdi_display, False); + ++ if (surface->hdc) NtUserReleaseDC(surface->hwnd, surface->hdc); ++ surface->hwnd_thread_id = 0; ++ surface->hwnd = 0; ++ surface->hdc = 0; ++ wine_vk_surface_release(surface); ++} ++ ++void destroy_vk_surface(HWND hwnd) ++{ ++ struct wine_vk_surface *surface, *next; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY_SAFE(surface, next, &surface_list, struct wine_vk_surface, entry) + { +- hdc = surface->hdc; +- surface->hwnd_thread_id = 0; +- surface->hwnd = 0; +- surface->hdc = 0; +- wine_vk_surface_release(surface); ++ if (surface->hwnd != hwnd) ++ continue; ++ wine_vk_surface_destroy(surface); + } +- XDeleteContext(gdi_display, (XID)hwnd, vulkan_hwnd_context); + pthread_mutex_unlock(&vulkan_mutex); +- if (hdc) NtUserReleaseDC(hwnd, hdc); + } + + static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL offscreen) +@@ -284,8 +289,12 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; + pthread_mutex_lock(&vulkan_mutex); +- if (!XFindContext(gdi_display, (XID)hwnd, vulkan_hwnd_context, (char **)&surface)) ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) ++ continue; + wine_vk_surface_set_offscreen(surface, known_child); ++ } + pthread_mutex_unlock(&vulkan_mutex); + } + +@@ -299,11 +308,7 @@ void vulkan_thread_detach(void) + { + if (surface->hwnd_thread_id != thread_id) + continue; +- +- TRACE("Detaching surface %p, hwnd %p.\n", surface, surface->hwnd); +- XReparentWindow(gdi_display, surface->window, get_dummy_parent(), 0, 0); +- XSync(gdi_display, False); +- wine_vk_surface_destroy(surface->hwnd); ++ wine_vk_surface_destroy(surface); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -475,6 +480,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { + res = VK_ERROR_INCOMPATIBLE_DRIVER; +@@ -496,11 +502,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- if (x11_surface->hwnd) +- { +- wine_vk_surface_destroy( x11_surface->hwnd ); +- XSaveContext(gdi_display, (XID)create_info->hwnd, vulkan_hwnd_context, (char *)wine_vk_surface_grab(x11_surface)); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 6fbf938a477..8438fdb7225 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1791,7 +1791,7 @@ void X11DRV_DestroyWindow( HWND hwnd ) + release_win_data( data ); + free( data ); + destroy_gl_drawable( hwnd ); +- wine_vk_surface_destroy( hwnd ); ++ destroy_vk_surface( hwnd ); + } + + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index eb38909a47d..5beaf0d10fc 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -630,7 +630,7 @@ extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; + extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; +-extern void wine_vk_surface_destroy( HWND hwnd ) DECLSPEC_HIDDEN; ++extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + +-- +2.37.1 + +From e75b04de0da8dc311f81ae2044cd073b68289f22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:12:16 +0200 +Subject: [PATCH 10/14] winex11.drv: Resize vulkan surfaces client rect size + changes. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 16 ++++++++++++++++ + dlls/winex11.drv/window.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 18 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index d3e554090c2..3abf84c6811 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -285,6 +285,18 @@ static BOOL wine_vk_surface_set_offscreen(struct wine_vk_surface *surface, BOOL + return !offscreen; + } + ++void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *changes) ++{ ++ struct wine_vk_surface *surface; ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface->window != active) XConfigureWindow(gdi_display, surface->window, mask, changes); ++ } ++ pthread_mutex_unlock(&vulkan_mutex); ++} ++ + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 8438fdb7225..b6087eac24c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1370,6 +1370,7 @@ static void sync_client_position( struct x11drv_win_data *data, + TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n", + data->client_window, changes.x, changes.y, changes.width, changes.height, mask ); + XConfigureWindow( gdi_display, data->client_window, mask, &changes ); ++ resize_vk_surfaces( data->hwnd, data->client_window, mask, &changes ); + } + } + +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5beaf0d10fc..801ec79559a 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -632,6 +632,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent ) DECLSPEC_HIDDEN; + extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; ++extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From 3603a55ba0806b2e6c8cf010aadf5438e881c84d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Oct 2021 00:22:00 +0200 +Subject: [PATCH 11/14] winex11.drv: Update client_window pointer on surface + destroy. + +To prevent reusing already destroyed client_window with the thread +display requests. + +This lets us restore another client window, as the primary client +window. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45277 +CW-Bug-Id: 16608 +--- + dlls/winex11.drv/vulkan.c | 25 +++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 16 ++++++++++++++++ + dlls/winex11.drv/x11drv.h | 2 ++ + 3 files changed, 43 insertions(+) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 3abf84c6811..2770123deba 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -310,6 +310,24 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + pthread_mutex_unlock(&vulkan_mutex); + } + ++Window wine_vk_active_surface( HWND hwnd ) ++{ ++ struct wine_vk_surface *surface, *active = NULL; ++ Window window; ++ ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ active = surface; ++ } ++ if (!active) window = None; ++ else window = active->window; ++ pthread_mutex_unlock(&vulkan_mutex); ++ ++ return window; ++} ++ + void vulkan_thread_detach(void) + { + struct wine_vk_surface *surface, *next; +@@ -541,6 +559,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + const VkAllocationCallbacks *allocator) + { + struct wine_vk_surface *x11_surface = surface_from_handle(surface); ++ HWND hwnd = x11_surface->hwnd; + + TRACE("%p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator); + +@@ -553,6 +572,7 @@ static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface + pvkDestroySurfaceKHR(instance, x11_surface->surface, NULL /* allocator */); + + wine_vk_surface_release(x11_surface); ++ update_client_window(hwnd); + } + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index b6087eac24c..9fdbfbeb68e 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1475,6 +1475,22 @@ Window get_dummy_parent(void) + } + + ++/********************************************************************** ++ * update_client_window ++ */ ++void update_client_window( HWND hwnd ) ++{ ++ struct x11drv_win_data *data; ++ if ((data = get_win_data( hwnd ))) ++ { ++ data->client_window = wine_vk_active_surface( hwnd ); ++ /* make sure any request that could use old client window has been flushed */ ++ XFlush( data->display ); ++ release_win_data( data ); ++ } ++} ++ ++ + /********************************************************************** + * create_dummy_client_window + */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 801ec79559a..c13bc0bdb16 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -633,6 +633,7 @@ extern void destroy_gl_drawable( HWND hwnd ) DECLSPEC_HIDDEN; + extern void destroy_vk_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void sync_vk_surface( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; + extern void resize_vk_surfaces( HWND hwnd, Window active, int mask, XWindowChanges *changes ) DECLSPEC_HIDDEN; ++extern Window wine_vk_active_surface( HWND hwnd ) DECLSPEC_HIDDEN; + extern void vulkan_thread_detach(void) DECLSPEC_HIDDEN; + + extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ) DECLSPEC_HIDDEN; +@@ -643,6 +644,7 @@ extern void update_net_wm_states( struct x11drv_win_data *data ) DECLSPEC_HIDDEN + extern void make_window_embedded( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; + extern Window create_dummy_client_window(void) DECLSPEC_HIDDEN; + extern Window create_client_window( HWND hwnd, const XVisualInfo *visual ) DECLSPEC_HIDDEN; ++extern void update_client_window( HWND hwnd ) DECLSPEC_HIDDEN; + extern void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha ) DECLSPEC_HIDDEN; + extern void change_systray_owner( Display *display, Window systray_window ) DECLSPEC_HIDDEN; + extern HWND create_foreign_window( Display *display, Window window ) DECLSPEC_HIDDEN; +-- +2.37.1 + +From dc43b1c5bb62726f29cea0312f89f838d98ef47d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:25 +0200 +Subject: [PATCH 12/14] winex11.drv: Support concurrent Vulkan surfaces using + XComposite. + +--- + dlls/winex11.drv/vulkan.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 2770123deba..77fd1b5ef66 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -64,6 +64,7 @@ struct wine_vk_surface + Window window; + VkSurfaceKHR surface; /* native surface */ + VkPresentModeKHR present_mode; ++ BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ + HDC hdc; + HWND hwnd; +@@ -300,12 +301,21 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; ++ DWORD surface_count = 0; ++ + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { +- if (surface->hwnd != hwnd) +- continue; +- wine_vk_surface_set_offscreen(surface, known_child); ++ if (surface->hwnd != hwnd) continue; ++ surface->known_child = known_child; ++ surface_count++; ++ } ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (surface->hwnd != hwnd) continue; ++ if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } +@@ -313,6 +323,7 @@ void sync_vk_surface(HWND hwnd, BOOL known_child) + Window wine_vk_active_surface( HWND hwnd ) + { + struct wine_vk_surface *surface, *active = NULL; ++ DWORD surface_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); +@@ -320,9 +331,16 @@ Window wine_vk_active_surface( HWND hwnd ) + { + if (surface->hwnd != hwnd) continue; + active = surface; ++ surface_count++; + } + if (!active) window = None; +- else window = active->window; ++ else ++ { ++ TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); ++ if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ else wine_vk_surface_set_offscreen(active, active->known_child); ++ window = active->window; ++ } + pthread_mutex_unlock(&vulkan_mutex); + + return window; +@@ -474,7 +492,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface; ++ struct wine_vk_surface *x11_surface, *other; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -487,6 +505,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; ++ x11_surface->known_child = FALSE; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -510,6 +529,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + if (create_info->hwnd && (NtUserGetWindowRelative(create_info->hwnd, GW_CHILD) || + NtUserGetAncestor(create_info->hwnd, GA_PARENT) != NtUserGetDesktopWindow())) + { ++ x11_surface->known_child = TRUE; + TRACE("hwnd %p creating offscreen child window surface\n", x11_surface->hwnd); + if (!wine_vk_surface_set_offscreen(x11_surface, TRUE)) + { +@@ -532,6 +552,13 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +-- +2.37.1 + +From be638810ad65e8d5a78ce90d19e1592012e07944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 22 Apr 2022 23:23:49 +0200 +Subject: [PATCH 13/14] winex11.drv: Consider only Vulkan surfaces with + swapchains for offscreen rendering. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"A native window cannot be associated with more than one non-retired +swapchain at a time."[1] + +The hack introduced in 81f5a09134e5 ("winex11: Allow multiple vulkan +surfaces per hwnd") sends surfaces for offscreen rendering using +XComposite when there are multiple surfaces associated with a single +hwnd. + +That's overzealous though, as some of the swapchains may be already +destroyed. + +E.g. DOOM Eternal with vsync enabled does the following: + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &old_surface) + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &old_sc); + vkDestroySwapchainKHR(vk_inst, old_sc, NULL); + /* old_surface never gets destroyed */ + + vkCreateWin32SurfaceKHR(vk_inst, &surface_create_info, NULL, &new_surface); + vkCreateSwapchainKHR(vk_inst, &sc_create_info, NULL, &new_swapchain); + +Which makes the hack kick in and degrades the performance. + +This change makes sure that we only count surfaces that have any +swapchains associated with them, whether they are retired or not. + +That's a bit of oversimplification, as swapchain can get retired without +new swapchain being created: + +"Upon calling vkCreateSwapchainKHR with an oldSwapchain that is not +VK_NULL_HANDLE, oldSwapchain is retired — even if creation of the new +swapchain fails. The new swapchain is created in the non-retired state +whether or not oldSwapchain is VK_NULL_HANDLE."[2] + +but that's unlikely to happen and cause problems. + +[1]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainKHR.html +[2]: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSwapchainCreateInfoKHR.html + +CW-Bug-Id: #19666 +--- + dlls/winex11.drv/vulkan.c | 47 ++++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 20 deletions(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 77fd1b5ef66..1275bc18aa0 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -66,6 +66,7 @@ struct wine_vk_surface + VkPresentModeKHR present_mode; + BOOL known_child; /* hwnd is or has a child */ + BOOL offscreen; /* drawable is offscreen */ ++ LONG swapchain_count; /* surface can have one active an many retired swapchains */ + HDC hdc; + HWND hwnd; + DWORD hwnd_thread_id; +@@ -301,43 +302,43 @@ void resize_vk_surfaces(HWND hwnd, Window active, int mask, XWindowChanges *chan + void sync_vk_surface(HWND hwnd, BOOL known_child) + { + struct wine_vk_surface *surface; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + surface->known_child = known_child; +- surface_count++; + } +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, known_child); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, known_child); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(surface, TRUE); + else wine_vk_surface_set_offscreen(surface, known_child); + } + pthread_mutex_unlock(&vulkan_mutex); + } + +-Window wine_vk_active_surface( HWND hwnd ) ++Window wine_vk_active_surface(HWND hwnd) + { + struct wine_vk_surface *surface, *active = NULL; +- DWORD surface_count = 0; ++ DWORD surface_with_swapchain_count = 0; + Window window; + + pthread_mutex_lock(&vulkan_mutex); + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; ++ if (surface->swapchain_count) surface_with_swapchain_count++; + active = surface; +- surface_count++; + } + if (!active) window = None; + else + { +- TRACE("hwnd %p surface_count %u known_child %u\n", hwnd, surface_count, active->known_child); +- if (surface_count > 1) wine_vk_surface_set_offscreen(active, TRUE); ++ TRACE("hwnd %p surface_with_swapchain_count %u known_child %u\n", hwnd, surface_with_swapchain_count, active->known_child); ++ if (surface_with_swapchain_count > 1) wine_vk_surface_set_offscreen(active, TRUE); + else wine_vk_surface_set_offscreen(active, active->known_child); + window = active->window; + } +@@ -455,7 +456,7 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain) + { +- struct wine_vk_surface *x11_surface = surface_from_handle(create_info->surface); ++ struct wine_vk_surface *other, *x11_surface = surface_from_handle(create_info->surface); + VkSwapchainCreateInfoKHR create_info_host; + VkResult result; + +@@ -476,13 +477,22 @@ static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device, + create_info_host.presentMode = VK_PRESENT_MODE_FIFO_KHR; + x11_surface->present_mode = create_info->presentMode; + ++ pthread_mutex_lock(&vulkan_mutex); ++ LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) ++ { ++ if (other->hwnd != x11_surface->hwnd) continue; ++ if (!other->swapchain_count) continue; ++ TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); ++ wine_vk_surface_set_offscreen(other, TRUE); ++ wine_vk_surface_set_offscreen(x11_surface, TRUE); ++ } + result = pvkCreateSwapchainKHR(device, &create_info_host, NULL /* allocator */, swapchain); + if (result == VK_SUCCESS) + { +- pthread_mutex_lock(&vulkan_mutex); ++ x11_surface->swapchain_count++; + XSaveContext(gdi_display, (XID)(*swapchain), vulkan_swapchain_context, (char *)wine_vk_surface_grab(x11_surface)); +- pthread_mutex_unlock(&vulkan_mutex); + } ++ pthread_mutex_unlock(&vulkan_mutex); + return result; + } + +@@ -492,7 +502,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + { + VkResult res; + VkXlibSurfaceCreateInfoKHR create_info_host; +- struct wine_vk_surface *x11_surface, *other; ++ struct wine_vk_surface *x11_surface; + + TRACE("%p %p %p %p\n", instance, create_info, allocator, surface); + +@@ -506,6 +516,7 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + x11_surface->ref = 1; + x11_surface->hwnd = create_info->hwnd; + x11_surface->known_child = FALSE; ++ x11_surface->swapchain_count = 0; + if (x11_surface->hwnd) + { + x11_surface->hdc = NtUserGetDCEx(x11_surface->hwnd, 0, DCX_USESTYLE); +@@ -552,13 +563,6 @@ static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance, + } + + pthread_mutex_lock(&vulkan_mutex); +- LIST_FOR_EACH_ENTRY(other, &surface_list, struct wine_vk_surface, entry) +- { +- if (other->hwnd != x11_surface->hwnd) continue; +- TRACE("hwnd %p already has a swapchain, moving surface offscreen\n", x11_surface->hwnd); +- wine_vk_surface_set_offscreen(other, TRUE); +- wine_vk_surface_set_offscreen(x11_surface, TRUE); +- } + list_add_tail(&surface_list, &x11_surface->entry); + pthread_mutex_unlock(&vulkan_mutex); + +@@ -617,7 +621,10 @@ static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapcha + + pthread_mutex_lock(&vulkan_mutex); + if (!XFindContext(gdi_display, (XID)swapchain, vulkan_swapchain_context, (char **)&surface)) ++ { ++ surface->swapchain_count--; + wine_vk_surface_release(surface); ++ } + XDeleteContext(gdi_display, (XID)swapchain, vulkan_swapchain_context); + pthread_mutex_unlock(&vulkan_mutex); + } +-- +2.37.1 + +From 58e87310c194fb3e460aef9325720dd7c9e5c3b3 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 23 Nov 2021 13:54:34 +0200 +Subject: [PATCH 14/14] winex11.drv: Don't consider swapchain-less Vulkan + surfaces active. + +--- + dlls/winex11.drv/vulkan.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 1275bc18aa0..f358d34e1e8 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -331,8 +331,9 @@ Window wine_vk_active_surface(HWND hwnd) + LIST_FOR_EACH_ENTRY(surface, &surface_list, struct wine_vk_surface, entry) + { + if (surface->hwnd != hwnd) continue; +- if (surface->swapchain_count) surface_with_swapchain_count++; ++ if (!surface->swapchain_count) continue; + active = surface; ++ surface_with_swapchain_count++; + } + if (!active) window = None; + else +-- +2.37.1 + +From 9897cbc555f30b3b8574d5b23297a303d34d73d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 22 Feb 2022 18:11:13 +0300 +Subject: [PATCH] winex11.drv: Also treat VK_SUBOPTIMAL_KHR as success in + X11DRV_vkAcquireNextImageKHR(). + +CW-Bug-Id: #20200 +--- + dlls/winex11.drv/vulkan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c +index 8c822722591..918f32350b1 100644 +--- a/dlls/winex11.drv/vulkan.c ++++ b/dlls/winex11.drv/vulkan.c +@@ -436,7 +436,7 @@ static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, + } + + result = pvkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, image_index); +- if (result == VK_SUCCESS && hdc && surface && surface->offscreen) ++ if ((result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) && hdc && surface && surface->offscreen) + { + if (wait_fence) pvkWaitForFences(device, 1, &fence, 0, timeout); + escape.code = X11DRV_PRESENT_DRAWABLE; +From 5b780cee2123052e2025bd8c7a2dc9781b6c1106 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 9 May 2022 12:44:00 -0500 +Subject: [PATCH] winex11.drv: Reparent client window back to whole window when + reactivating it. + +CW-Bug-Id: #20614 + +Related to Vulkan child window / multiple swapchains handling, +specificall this commit: +"winex11.drv: Update client_window pointer on surface destroy." + +When the client window is replaced by the new one it gets reparented to +dummy window (effectively hidden). When another swapchain gets deleted +and the previous client window is brought back onscreen, in the current +logic it makes sense to make it visible child of the whole window again. +--- + dlls/winex11.drv/window.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index e1d84c61575..fa22dd890d1 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1749,9 +1749,19 @@ Window get_dummy_parent(void) + void update_client_window( HWND hwnd ) + { + struct x11drv_win_data *data; ++ Window old_active; ++ + if ((data = get_win_data( hwnd ))) + { ++ old_active = data->client_window; + data->client_window = wine_vk_active_surface( hwnd ); ++ if (data->client_window && data->whole_window && old_active != data->client_window) ++ { ++ TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window ); ++ XReparentWindow( gdi_display, data->client_window, data->whole_window, ++ data->client_rect.left - data->whole_rect.left, ++ data->client_rect.top - data->whole_rect.top ); ++ } + /* make sure any request that could use old client window has been flushed */ + XFlush( data->display ); + release_win_data( data ); +From fa9a87cdc728eb5bc6435bbe7c602e1c2a55b9d8 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 20 May 2022 16:20:29 -0500 +Subject: [PATCH] winex11.drv: Also call sync_vk_surface() for the child window + when WS_CHILD is changed. + +CW-Bug-Id: #19945 +--- + dlls/winex11.drv/window.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 521342d3e84..8fc05079d76 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2106,6 +2106,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) + sync_vk_surface( parent, TRUE ); + else + sync_vk_surface( parent, FALSE ); ++ sync_vk_surface( hwnd, style->styleNew & WS_CHILD ); + } + + if (hwnd == NtUserGetDesktopWindow()) return; diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync index ee30d7a9a..9eca43171 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync @@ -21,14 +21,26 @@ if [ "$_use_staging" = "true" ]; then if [ "$_staging_esync" = "true" ] && [ "$_use_fsync" = "true" ]; then if [ "$_protonify" = "true" ]; then - if ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor e534d6546a5be9f1cd53a0ea3ac79db7d977bed7 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then _patchname='fastsync-staging-protonify.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 7b243afc6322fa82ac42cec6de247a642e16b5a5 HEAD ); then + _patchname='fastsync-staging-protonify-59485f0.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 03c1930b74e9a41da84fd712a3ebdbb4f7fcbcb5 HEAD ); then + _patchname='fastsync-staging-protonify-7b243af.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor e534d6546a5be9f1cd53a0ea3ac79db7d977bed7 HEAD ); then + _patchname='fastsync-staging-protonify-03c1930.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 36b45c6d1c124dd16b3475ba743fcbbc99d6862d HEAD ); then _patchname='fastsync-staging-protonify-e534d65.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher fi else - if ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor e534d6546a5be9f1cd53a0ea3ac79db7d977bed7 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then _patchname='fastsync-staging.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 7b243afc6322fa82ac42cec6de247a642e16b5a5 HEAD ); then + _patchname='fastsync-staging-59485f0.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 03c1930b74e9a41da84fd712a3ebdbb4f7fcbcb5 HEAD ); then + _patchname='fastsync-staging-7b243af.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor e534d6546a5be9f1cd53a0ea3ac79db7d977bed7 HEAD ); then + _patchname='fastsync-staging-03c1930.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 36b45c6d1c124dd16b3475ba743fcbbc99d6862d HEAD ); then _patchname='fastsync-staging-e534d65.patch' && _patchmsg="Using fastsync (Esync/Fsync compatible) patchset" && nonuser_patcher fi @@ -39,8 +51,14 @@ fi else if [ "$_use_esync" = "false" ] && [ "$_use_fsync" = "false" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f076e5f85490a44fd34057df9af1c3ae3e7d5d3b HEAD); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD); then _patchname='fastsync-mainline.patch' && _patchmsg="Using fastsync (mainline) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 7b243afc6322fa82ac42cec6de247a642e16b5a5 HEAD); then + _patchname='fastsync-mainline-59485f0.patch' && _patchmsg="Using fastsync (mainline) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 03c1930b74e9a41da84fd712a3ebdbb4f7fcbcb5 HEAD ); then + _patchname='fastsync-mainline-7b243af.patch' && _patchmsg="Using fastsync (mainline) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f076e5f85490a44fd34057df9af1c3ae3e7d5d3b HEAD ); then + _patchname='fastsync-mainline-03c1930.patch' && _patchmsg="Using fastsync (mainline) patchset" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 36b45c6d1c124dd16b3475ba743fcbbc99d6862d HEAD ); then _patchname='fastsync-mainline-f076e5f.patch' && _patchmsg="Using fastsync (mainline) patchset" && nonuser_patcher fi diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-mainline.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-mainline.patch index aedec9881..9080661bb 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-mainline.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-mainline.patch @@ -430,7 +430,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 pipe_server_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -438,7 +438,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = @@ -784,9 +784,9 @@ index 6cbc0a61be1..98b7746c726 100644 linux/types.h \ linux/ucdrom.h \ + linux/winesync.h \ + linux/wireless.h \ lwp.h \ mach-o/loader.h \ - mach/mach.h \ diff --git a/server/Makefile.in b/server/Makefile.in index 739d0517339..919e652eceb 100644 --- a/server/Makefile.in @@ -2937,7 +2937,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -2946,7 +2946,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = @@ -3437,13 +3437,13 @@ index 795fc148479..adcca4f4f1f 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz - timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ); extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + const LARGE_INTEGER *timeout ); +extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ const LARGE_INTEGER *timeout ); extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, - apc_result_t *result ) DECLSPEC_HIDDEN; + apc_result_t *result ); extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, @@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use @@ -5002,23 +5002,23 @@ index 9158a4736ad..42943d6a84a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W - extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; - extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + extern NTSTATUS load_start_exe( WCHAR **image, void **module ); + extern void start_server( BOOL debug ); -+extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++extern pthread_mutex_t fd_cache_mutex; + - extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; - extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; - extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern unsigned int server_call_unlocked( void *req_ptr ); + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); @@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U - extern void dbg_init(void) DECLSPEC_HIDDEN; + extern void dbg_init(void); -+extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++extern void close_fast_sync_obj( HANDLE handle ); + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; - extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; + PNTAPCFUNC func, NTSTATUS status ); + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); -- 2.35.3 diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging-protonify.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging-protonify.patch index 24941f988..751e177bc 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging-protonify.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging-protonify.patch @@ -454,7 +454,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 pipe_server_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -462,7 +462,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = @@ -808,9 +808,9 @@ index 6cbc0a61be1..98b7746c726 100644 linux/types.h \ linux/ucdrom.h \ + linux/winesync.h \ + linux/wireless.h \ lwp.h \ mach-o/loader.h \ - mach/mach.h \ diff --git a/server/Makefile.in b/server/Makefile.in index 739d0517339..919e652eceb 100644 --- a/server/Makefile.in @@ -2982,7 +2982,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -2991,7 +2991,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = @@ -3477,13 +3477,13 @@ index 795fc148479..adcca4f4f1f 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz - timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ); extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + const LARGE_INTEGER *timeout ); +extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ const LARGE_INTEGER *timeout ); extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, - apc_result_t *result ) DECLSPEC_HIDDEN; + apc_result_t *result ); extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, @@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use @@ -5040,23 +5040,23 @@ index 9158a4736ad..42943d6a84a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W - extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; - extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + extern NTSTATUS load_start_exe( WCHAR **image, void **module ); + extern void start_server( BOOL debug ); -+extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++extern pthread_mutex_t fd_cache_mutex; + - extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; - extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; - extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern unsigned int server_call_unlocked( void *req_ptr ); + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); @@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U - extern void dbg_init(void) DECLSPEC_HIDDEN; + extern void dbg_init(void); -+extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++extern void close_fast_sync_obj( HANDLE handle ); + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; - extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; + PNTAPCFUNC func, NTSTATUS status ); + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); -- 2.35.3 diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging.patch index e88bcad9a..c7de69fa4 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging.patch +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync-staging.patch @@ -454,7 +454,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 pipe_server_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -462,7 +462,7 @@ index 3e6cf09d4f2..dec4f95c6c3 100644 no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ + no_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = @@ -808,9 +808,9 @@ index 6cbc0a61be1..98b7746c726 100644 linux/types.h \ linux/ucdrom.h \ + linux/winesync.h \ + linux/wireless.h \ lwp.h \ mach-o/loader.h \ - mach/mach.h \ diff --git a/server/Makefile.in b/server/Makefile.in index 739d0517339..919e652eceb 100644 --- a/server/Makefile.in @@ -2981,7 +2981,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = @@ -2990,7 +2990,7 @@ index dec4f95c6c3..44d3de4cadd 100644 no_kernel_obj_list, /* get_kernel_obj_list */ - no_get_fast_sync, /* get_fast_sync */ + default_fd_get_fast_sync, /* get_fast_sync */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; @@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = @@ -3476,13 +3476,13 @@ index 795fc148479..adcca4f4f1f 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz - timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ); extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + const LARGE_INTEGER *timeout ); +extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ const LARGE_INTEGER *timeout ); extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, - apc_result_t *result ) DECLSPEC_HIDDEN; + apc_result_t *result ); extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, @@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use @@ -5039,23 +5039,23 @@ index 9158a4736ad..42943d6a84a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W - extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; - extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + extern NTSTATUS load_start_exe( WCHAR **image, void **module ); + extern void start_server( BOOL debug ); -+extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++extern pthread_mutex_t fd_cache_mutex; + - extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; - extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; - extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern unsigned int server_call_unlocked( void *req_ptr ); + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ); @@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U - extern void dbg_init(void) DECLSPEC_HIDDEN; + extern void dbg_init(void); -+extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++extern void close_fast_sync_obj( HANDLE handle ); + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; - extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; + PNTAPCFUNC func, NTSTATUS status ); + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); -- 2.35.3 diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-03c1930.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-03c1930.patch new file mode 100644 index 000000000..aedec9881 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-03c1930.patch @@ -0,0 +1,5475 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + obj_handle_t handle; /* next thread handle */ + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + device.c \ + directory.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + handle.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -84,9 +87,9 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -152,6 +155,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,11 +179,13 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) + { + event->signaled = 0; ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,6 +231,26 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,298 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + unsigned int once_input : 1; /* flag if input thread has already been requested */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + assert( obj->ops == &console_server_ops ); + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,8 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + set_error( STATUS_PENDING ); + } + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,10 +93,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->trace_data = 0; + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; ++ process->fast_sync = NULL; + memset( &process->image_info, 0, sizeof(process->image_info) ); + list_init( &process->kernel_object ); + list_init( &process->thread_list ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + if (process->token) release_object( process->token ); + free( process->dir_cache ); + free( process->image ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + pe_image_info_t image_info; /* main exe image info */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->keystate_lock = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + { + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + if (queue->keystate_lock) unlock_input_keystate( queue->input ); + queue->keystate_lock = 0; + } ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + release_object( queue->input ); + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + } + } +@@ -2441,6 +2475,8 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + } + +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->token = NULL; + thread->desc = NULL; + thread->desc_len = 0; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,7 @@ static void destroy_thread( struct object *obj ) + release_object( thread->process ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,6 +1306,7 @@ void kill_thread( struct thread *thread, int violent_death ) + abandon_mutexes( thread ); + fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + struct list kernel_object; /* list of kernel object pointers */ + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,11 +61,13 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->fast_sync = NULL; + } + } + return timer; +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,7 @@ static void fd_destroy( struct object *obj ) + if (fd->unix_fd != -1) close( fd->unix_fd ); + free( fd->unix_name ); + } ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + } + + /* check if events are pending and if yes return which one(s) */ +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->last_get_msg = current_time; + queue->keystate_lock = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,8 +1164,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); ++ } ++ + return 1; + } + +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + return apc; + } +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,13 +1442,23 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + + TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + + TRACE( "handle %p, prev_count %p\n", handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,11 +2301,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -106,7 +106,7 @@ sigset_t server_block_set; /* signals to block during server calls */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static int initial_cwd = -1; + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,12 +1727,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + fd = remove_fd_from_cache( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From d49981f72833501466dd2e787f6d91e55abcaff9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 26 Jun 2018 18:44:44 -0500 +Subject: [PATCH 27/30] kernel32/tests: Zigzag test. + +The primary function is to check for races. The secondary function is to measure performance. +--- + dlls/kernel32/tests/sync.c | 79 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 93cae1c84d5..12fae701db9 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -2778,6 +2778,84 @@ static void test_QueueUserAPC(void) + ok(apc_count == 1, "APC count %u\n", apc_count); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%d\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %u\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %u\n", ret); ++ ++ trace("count: %d\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2843,5 +2921,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_next_thread_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_suspend_process, + REQ_resume_process, + REQ_get_next_thread, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct suspend_process_request suspend_process_request; + struct resume_process_request resume_process_request; + struct get_next_thread_request get_next_thread_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct suspend_process_reply suspend_process_reply; + struct resume_process_reply resume_process_reply; + struct get_next_thread_reply get_next_thread_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(suspend_process); + DECL_HANDLER(resume_process); + DECL_HANDLER(get_next_thread); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_suspend_process, + (req_handler)req_resume_process, + (req_handler)req_get_next_thread, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( sizeof(struct get_next_thread_request) == 32 ); + C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 ); + C_ASSERT( sizeof(struct get_next_thread_reply) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req + fprintf( stderr, " handle=%04x", req->handle ); + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_suspend_process_request, + (dump_func)dump_resume_process_request, + (dump_func)dump_get_next_thread_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + NULL, + NULL, + (dump_func)dump_get_next_thread_reply, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "suspend_process", + "resume_process", + "get_next_thread", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-59485f0.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-59485f0.patch new file mode 100644 index 000000000..8a176b369 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-59485f0.patch @@ -0,0 +1,5475 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + obj_handle_t handle; /* next thread handle */ + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + linux/wireless.h \ + lwp.h \ + mach-o/loader.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + device.c \ + directory.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + handle.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -84,9 +87,9 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -152,6 +155,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,11 +179,13 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) + { + event->signaled = 0; ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,6 +231,26 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,298 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + unsigned int once_input : 1; /* flag if input thread has already been requested */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + assert( obj->ops == &console_server_ops ); + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,8 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + set_error( STATUS_PENDING ); + } + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,10 +93,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->trace_data = 0; + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; ++ process->fast_sync = NULL; + memset( &process->image_info, 0, sizeof(process->image_info) ); + list_init( &process->kernel_object ); + list_init( &process->thread_list ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + if (process->token) release_object( process->token ); + free( process->dir_cache ); + free( process->image ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + pe_image_info_t image_info; /* main exe image info */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->keystate_lock = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + { + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + if (queue->keystate_lock) unlock_input_keystate( queue->input ); + queue->keystate_lock = 0; + } ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + release_object( queue->input ); + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + } + } +@@ -2441,6 +2475,8 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + } + +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->token = NULL; + thread->desc = NULL; + thread->desc_len = 0; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,7 @@ static void destroy_thread( struct object *obj ) + release_object( thread->process ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,6 +1306,7 @@ void kill_thread( struct thread *thread, int violent_death ) + abandon_mutexes( thread ); + fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + struct list kernel_object; /* list of kernel object pointers */ + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,11 +61,13 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->fast_sync = NULL; + } + } + return timer; +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,7 @@ static void fd_destroy( struct object *obj ) + if (fd->unix_fd != -1) close( fd->unix_fd ); + free( fd->unix_name ); + } ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + } + + /* check if events are pending and if yes return which one(s) */ +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->last_get_msg = current_time; + queue->keystate_lock = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,8 +1164,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); ++ } ++ + return 1; + } + +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + return apc; + } +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,13 +1442,23 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + + TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + + TRACE( "handle %p, prev_count %p\n", handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,11 +2301,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -106,7 +106,7 @@ sigset_t server_block_set; /* signals to block during server calls */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static int initial_cwd = -1; + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,12 +1727,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + fd = remove_fd_from_cache( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From d49981f72833501466dd2e787f6d91e55abcaff9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 26 Jun 2018 18:44:44 -0500 +Subject: [PATCH 27/30] kernel32/tests: Zigzag test. + +The primary function is to check for races. The secondary function is to measure performance. +--- + dlls/kernel32/tests/sync.c | 79 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 93cae1c84d5..12fae701db9 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -2778,6 +2778,84 @@ static void test_QueueUserAPC(void) + ok(apc_count == 1, "APC count %u\n", apc_count); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%d\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %u\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %u\n", ret); ++ ++ trace("count: %d\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2843,5 +2921,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_next_thread_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_suspend_process, + REQ_resume_process, + REQ_get_next_thread, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct suspend_process_request suspend_process_request; + struct resume_process_request resume_process_request; + struct get_next_thread_request get_next_thread_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct suspend_process_reply suspend_process_reply; + struct resume_process_reply resume_process_reply; + struct get_next_thread_reply get_next_thread_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(suspend_process); + DECL_HANDLER(resume_process); + DECL_HANDLER(get_next_thread); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_suspend_process, + (req_handler)req_resume_process, + (req_handler)req_get_next_thread, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( sizeof(struct get_next_thread_request) == 32 ); + C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 ); + C_ASSERT( sizeof(struct get_next_thread_reply) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req + fprintf( stderr, " handle=%04x", req->handle ); + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_suspend_process_request, + (dump_func)dump_resume_process_request, + (dump_func)dump_get_next_thread_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + NULL, + NULL, + (dump_func)dump_get_next_thread_reply, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "suspend_process", + "resume_process", + "get_next_thread", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-7b243af.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-7b243af.patch new file mode 100644 index 000000000..ee0e3f0cb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-mainline-7b243af.patch @@ -0,0 +1,5475 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + obj_handle_t handle; /* next thread handle */ + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + device.c \ + directory.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + handle.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,8 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -84,9 +87,9 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -152,6 +155,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,11 +179,13 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) + { + event->signaled = 0; ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,6 +231,26 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,298 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + unsigned int once_input : 1; /* flag if input thread has already been requested */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + assert( obj->ops == &console_server_ops ); + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,8 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + set_error( STATUS_PENDING ); + } + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,10 +93,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->trace_data = 0; + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; ++ process->fast_sync = NULL; + memset( &process->image_info, 0, sizeof(process->image_info) ); + list_init( &process->kernel_object ); + list_init( &process->thread_list ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + if (process->token) release_object( process->token ); + free( process->dir_cache ); + free( process->image ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + pe_image_info_t image_info; /* main exe image info */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->keystate_lock = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + { + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + if (queue->keystate_lock) unlock_input_keystate( queue->input ); + queue->keystate_lock = 0; + } ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + release_object( queue->input ); + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + } + } +@@ -2441,6 +2475,8 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + } + +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->token = NULL; + thread->desc = NULL; + thread->desc_len = 0; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,7 @@ static void destroy_thread( struct object *obj ) + release_object( thread->process ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,6 +1306,7 @@ void kill_thread( struct thread *thread, int violent_death ) + abandon_mutexes( thread ); + fast_abandon_mutexes( thread->id ); + wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + struct list kernel_object; /* list of kernel object pointers */ + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,11 +61,13 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->fast_sync = NULL; + } + } + return timer; +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,7 @@ static void fd_destroy( struct object *obj ) + if (fd->unix_fd != -1) close( fd->unix_fd ); + free( fd->unix_name ); + } ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->fast_sync = NULL; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + } + + /* check if events are pending and if yes return which one(s) */ +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->last_get_msg = current_time; + queue->keystate_lock = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,8 +1164,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); ++ } ++ + return 1; + } + +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + return apc; + } +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,13 +1442,23 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + + TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + + TRACE( "handle %p, prev_state %p\n", handle, prev_state ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + + TRACE( "handle %p, prev_count %p\n", handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,11 +2301,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -106,7 +106,7 @@ sigset_t server_block_set; /* signals to block during server calls */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static int initial_cwd = -1; + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,12 +1727,16 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + fd = remove_fd_from_cache( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From d49981f72833501466dd2e787f6d91e55abcaff9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 26 Jun 2018 18:44:44 -0500 +Subject: [PATCH 27/30] kernel32/tests: Zigzag test. + +The primary function is to check for races. The secondary function is to measure performance. +--- + dlls/kernel32/tests/sync.c | 79 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 93cae1c84d5..12fae701db9 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -2778,6 +2778,84 @@ static void test_QueueUserAPC(void) + ok(apc_count == 1, "APC count %u\n", apc_count); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%d\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %u\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %u\n", ret); ++ ++ trace("count: %d\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2843,5 +2921,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_next_thread_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_suspend_process, + REQ_resume_process, + REQ_get_next_thread, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct suspend_process_request suspend_process_request; + struct resume_process_request resume_process_request; + struct get_next_thread_request get_next_thread_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct suspend_process_reply suspend_process_reply; + struct resume_process_reply resume_process_reply; + struct get_next_thread_reply get_next_thread_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(suspend_process); + DECL_HANDLER(resume_process); + DECL_HANDLER(get_next_thread); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_suspend_process, + (req_handler)req_resume_process, + (req_handler)req_get_next_thread, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( sizeof(struct get_next_thread_request) == 32 ); + C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 ); + C_ASSERT( sizeof(struct get_next_thread_reply) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req + fprintf( stderr, " handle=%04x", req->handle ); + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_suspend_process_request, + (dump_func)dump_resume_process_request, + (dump_func)dump_get_next_thread_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + NULL, + NULL, + (dump_func)dump_get_next_thread_reply, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "suspend_process", + "resume_process", + "get_next_thread", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-03c1930.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-03c1930.patch new file mode 100644 index 000000000..e88bcad9a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-03c1930.patch @@ -0,0 +1,5480 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + memset( &process->image_info, 0, sizeof(process->image_info) ); + process->esync_fd = -1; + process->fsync_idx = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + pe_image_info_t image_info; /* main exe image info */ + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-59485f0.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-59485f0.patch new file mode 100644 index 000000000..d9dee86c1 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-59485f0.patch @@ -0,0 +1,5480 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + linux/wireless.h \ + lwp.h \ + mach-o/loader.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + memset( &process->image_info, 0, sizeof(process->image_info) ); + process->esync_fd = -1; + process->fsync_idx = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + pe_image_info_t image_info; /* main exe image info */ + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-7b243af.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-7b243af.patch new file mode 100644 index 000000000..d05aadeca --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-7b243af.patch @@ -0,0 +1,5480 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,10 +61,12 @@ struct completion + struct object obj; + struct list queue; + unsigned int depth; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + memset( &process->image_info, 0, sizeof(process->image_info) ); + process->esync_fd = -1; + process->fsync_idx = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + pe_image_info_t image_info; /* main exe image info */ + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-03c1930.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-03c1930.patch new file mode 100644 index 000000000..24941f988 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-03c1930.patch @@ -0,0 +1,5481 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,11 +61,13 @@ struct completion + struct list queue; + unsigned int depth; + int abandoned; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int completion_close( struct object *obj, struct process *process, obj_handle_t handle ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->esync_fd = -1; + process->fsync_idx = 0; + process->cpu_override.cpu_count = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; + struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-59485f0.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-59485f0.patch new file mode 100644 index 000000000..4f92a1c3b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-59485f0.patch @@ -0,0 +1,5481 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + linux/wireless.h \ + lwp.h \ + mach-o/loader.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,11 +61,13 @@ struct completion + struct list queue; + unsigned int depth; + int abandoned; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int completion_close( struct object *obj, struct process *process, obj_handle_t handle ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->esync_fd = -1; + process->fsync_idx = 0; + process->cpu_override.cpu_count = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; + struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-7b243af.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-7b243af.patch new file mode 100644 index 000000000..d6318eaba --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/legacy/fastsync-staging-protonify-7b243af.patch @@ -0,0 +1,5481 @@ +From 0236b9672602425191dce5d14c5188db0b7860ed Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 14:31:06 -0600 +Subject: [PATCH 01/30] server: Add an object operation to retrieve a fast + synchronization object. + +--- + server/async.c | 2 ++ + server/atom.c | 1 + + server/change.c | 1 + + server/clipboard.c | 1 + + server/completion.c | 1 + + server/console.c | 7 +++++++ + server/debugger.c | 2 ++ + server/device.c | 4 ++++ + server/directory.c | 2 ++ + server/event.c | 2 ++ + server/fd.c | 4 ++++ + server/file.c | 1 + + server/handle.c | 1 + + server/hook.c | 1 + + server/mailslot.c | 4 ++++ + server/mapping.c | 3 +++ + server/mutex.c | 1 + + server/named_pipe.c | 5 +++++ + server/object.c | 6 ++++++ + server/object.h | 7 +++++++ + server/process.c | 3 +++ + server/protocol.def | 12 ++++++++++++ + server/queue.c | 2 ++ + server/registry.c | 1 + + server/request.c | 1 + + server/semaphore.c | 1 + + server/serial.c | 1 + + server/signal.c | 1 + + server/sock.c | 3 +++ + server/symlink.c | 1 + + server/thread.c | 3 +++ + server/timer.c | 1 + + server/token.c | 1 + + server/window.c | 1 + + server/winstation.c | 2 ++ + 35 files changed, 90 insertions(+) + +diff --git a/server/async.c b/server/async.c +index a4fbeab555e..ede45653db2 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -89,6 +89,7 @@ static const struct object_ops async_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + async_destroy /* destroy */ + }; +@@ -629,6 +630,7 @@ static const struct object_ops iosb_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + iosb_destroy /* destroy */ + }; +diff --git a/server/atom.c b/server/atom.c +index ff0799f5880..ba320c4c630 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -91,6 +91,7 @@ static const struct object_ops atom_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + atom_table_destroy /* destroy */ + }; +diff --git a/server/change.c b/server/change.c +index 6477b457f74..c01dcd84b49 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,6 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/clipboard.c b/server/clipboard.c +index 8118a467dd8..de9f84f74d0 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -88,6 +88,7 @@ static const struct object_ops clipboard_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + clipboard_destroy /* destroy */ + }; +diff --git a/server/completion.c b/server/completion.c +index 6933195e72d..dd16787c63c 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -87,6 +87,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +diff --git a/server/console.c b/server/console.c +index 5f3f50d006f..2c62f7518d5 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -93,6 +93,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -170,6 +171,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -239,6 +241,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +291,7 @@ static const struct object_ops console_device_ops = + default_unlink_name, /* unlink_name */ + console_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -325,6 +329,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -382,6 +387,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +446,7 @@ static const struct object_ops console_connection_ops = + default_unlink_name, /* unlink_name */ + console_connection_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + console_connection_close_handle, /* close_handle */ + console_connection_destroy /* destroy */ + }; +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..80c2b5389c6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -98,6 +98,7 @@ static const struct object_ops debug_event_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_event_destroy /* destroy */ + }; +@@ -126,6 +127,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..691c0eb6b5f 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -78,6 +78,7 @@ static const struct object_ops irp_call_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + irp_call_destroy /* destroy */ + }; +@@ -118,6 +119,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +177,7 @@ static const struct object_ops device_ops = + default_unlink_name, /* unlink_name */ + device_open_file, /* open_file */ + device_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -227,6 +230,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/directory.c b/server/directory.c +index 23d7eb0a2b7..8e32abbcff2 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -81,6 +81,7 @@ static const struct object_ops object_type_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops directory_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + directory_destroy /* destroy */ + }; +diff --git a/server/esync.c b/server/esync.c +index 94f10d338b5..20bdddfc4da 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -144,6 +144,7 @@ const struct object_ops esync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + esync_destroy /* destroy */ + }; +diff --git a/server/event.c b/server/event.c +index f1b79b1b35e..d2ed6ae3df7 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -84,6 +84,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -131,6 +132,7 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 1b4b98b0e76..6b0693507e2 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -218,6 +218,7 @@ static const struct object_ops fd_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fd_destroy /* destroy */ + }; +@@ -259,6 +260,7 @@ static const struct object_ops device_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_destroy /* destroy */ + }; +@@ -299,6 +301,7 @@ static const struct object_ops inode_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + inode_destroy /* destroy */ + }; +@@ -341,6 +344,7 @@ static const struct object_ops file_lock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/file.c b/server/file.c +index eb2dc5696ed..e572b9f3c36 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,6 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/fsync.c b/server/fsync.c +index 45c3fc9eeb1..ac97ab711ce 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -158,6 +158,7 @@ const struct object_ops fsync_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + fsync_destroy /* destroy */ + }; +diff --git a/server/handle.c b/server/handle.c +index 38ad80da267..c652c04ac24 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -138,6 +138,7 @@ static const struct object_ops handle_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handle_table_destroy /* destroy */ + }; +diff --git a/server/hook.c b/server/hook.c +index 5abdf39ad37..5a00699283d 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -92,6 +92,7 @@ static const struct object_ops hook_table_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + hook_table_destroy /* destroy */ + }; +diff --git a/server/mailslot.c b/server/mailslot.c +index 2d8697ec9bd..d58c95042a0 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,6 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -145,6 +146,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mail_writer_destroy /* destroy */ + }; +@@ -208,6 +210,7 @@ static const struct object_ops mailslot_device_ops = + default_unlink_name, /* unlink_name */ + mailslot_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_destroy /* destroy */ + }; +@@ -238,6 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/mapping.c b/server/mapping.c +index 8d4332d240f..f8ab095d7d3 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -79,6 +79,7 @@ static const struct object_ops ranges_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ranges_destroy /* destroy */ + }; +@@ -115,6 +116,7 @@ static const struct object_ops shared_map_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + shared_map_destroy /* destroy */ + }; +@@ -188,5 +190,6 @@ static const struct object_ops mapping_ops = ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mapping_destroy /* destroy */ + }; + + static const struct fd_ops mapping_fd_ops = +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..f7ad1e800c9 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -85,6 +85,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index 3e6cf09d4f2..dec4f95c6c3 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -131,6 +131,7 @@ static const struct object_ops named_pipe_ops = + default_unlink_name, /* unlink_name */ + named_pipe_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_destroy /* destroy */ + }; +@@ -179,6 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -223,6 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -270,6 +273,7 @@ static const struct object_ops named_pipe_device_ops = + default_unlink_name, /* unlink_name */ + named_pipe_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_destroy /* destroy */ + }; +@@ -301,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/object.c b/server/object.c +index 907bc087444..65616353d89 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -528,6 +528,12 @@ struct fd *no_get_fd( struct object *obj ) + return NULL; + } + ++struct fast_sync *no_get_fast_sync( struct object *obj ) ++{ ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++} ++ + unsigned int default_map_access( struct object *obj, unsigned int access ) + { + return map_access( access, &obj->ops->type->mapping ); +diff --git a/server/object.h b/server/object.h +index f156f1d2f13..89ca434bd62 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -42,6 +42,7 @@ struct async; + struct async_queue; + struct winstation; + struct object_type; ++struct fast_sync; + + + struct unicode_str +@@ -103,6 +104,8 @@ struct object_ops + unsigned int options); + /* return list of kernel objects */ + struct list *(*get_kernel_obj_list)(struct object *); ++ /* get a client-waitable fast-synchronization handle to this object */ ++ struct fast_sync *(*get_fast_sync)(struct object *); + /* close a handle to this object */ + int (*close_handle)(struct object *,struct process *,obj_handle_t); + /* destroy on refcount == 0 */ +@@ -219,6 +222,10 @@ extern void reset_event( struct event *event ); + + extern void abandon_mutexes( struct thread *thread ); + ++/* fast-synchronization functions */ ++ ++extern struct fast_sync *no_get_fast_sync( struct object *obj ); ++ + /* serial functions */ + + int get_serial_async_timeout(struct object *obj, int type, int count); +diff --git a/server/process.c b/server/process.c +index d1407204718..4d146dd79fc 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -117,6 +117,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +169,7 @@ static const struct object_ops startup_info_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + startup_info_destroy /* destroy */ + }; +@@ -229,6 +231,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +diff --git a/server/protocol.def b/server/protocol.def +index 9b7b99ae86a..ba8c0c1e363 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3746,3 +3746,15 @@ struct handle_info + @REPLY + unsigned int shm_idx; + @END ++ ++ ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; +diff --git a/server/queue.c b/server/queue.c +index 4f69a082b74..e013f4fb32c 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -181,6 +181,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -218,6 +219,7 @@ static const struct object_ops thread_input_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_input_destroy /* destroy */ + }; +diff --git a/server/registry.c b/server/registry.c +index 93e8a309593..245812a25e1 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -190,6 +190,7 @@ static const struct object_ops key_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + key_close_handle, /* close_handle */ + key_destroy /* destroy */ + }; +diff --git a/server/request.c b/server/request.c +index 7021741c765..8c50f993d25 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -102,6 +102,7 @@ static const struct object_ops master_socket_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + master_socket_destroy /* destroy */ + }; +diff --git a/server/semaphore.c b/server/semaphore.c +index 53b42a886df..1a89bd0886b 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -82,6 +82,7 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..b2a1f79a211 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,6 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/signal.c b/server/signal.c +index 19b76d44c16..e5def3dc899 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -74,6 +74,7 @@ static const struct object_ops handler_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + handler_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index b403541fcbf..ef250e725c1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,6 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3168,6 +3169,7 @@ static const struct object_ops ifchange_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + ifchange_destroy /* destroy */ + }; +@@ -3389,6 +3391,7 @@ static const struct object_ops socket_device_ops = + default_unlink_name, /* unlink_name */ + socket_device_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +diff --git a/server/symlink.c b/server/symlink.c +index 27d48e2f994..81d128bd396 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -83,6 +83,7 @@ static const struct object_ops symlink_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + symlink_destroy /* destroy */ + }; +diff --git a/server/thread.c b/server/thread.c +index 467ccd1f0db..5b78bbc3ff9 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -108,6 +108,7 @@ static const struct object_ops thread_apc_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + thread_apc_destroy /* destroy */ + }; +@@ -153,6 +154,7 @@ static const struct object_ops context_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ + }; +@@ -202,6 +204,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..d4a69bf8794 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -88,6 +88,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +diff --git a/server/token.c b/server/token.c +index dcb2a879ba6..3447f93c21b 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -162,6 +162,7 @@ static const struct object_ops token_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + token_destroy /* destroy */ + }; +diff --git a/server/window.c b/server/window.c +index 7675cd1103d..6cf17a905f5 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -119,6 +119,7 @@ static const struct object_ops window_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + window_destroy /* destroy */ + }; +diff --git a/server/winstation.c b/server/winstation.c +index 1408e1a9e65..0fcb403ecfb 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -87,6 +87,7 @@ static const struct object_ops winstation_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + winstation_close_handle, /* close_handle */ + winstation_destroy /* destroy */ + }; +@@ -127,6 +128,7 @@ static const struct object_ops desktop_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ + desktop_close_handle, /* close_handle */ + desktop_destroy /* destroy */ + }; +-- +2.35.3 + +From bb891fae29657ef6110f2be57a5dc2a05f46a563 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:38:18 -0600 +Subject: [PATCH 02/30] server: Create fast synchronization objects for events. + +--- + configure.ac | 1 + + server/Makefile.in | 1 + + server/event.c | 30 ++++- + server/fast_sync.c | 297 +++++++++++++++++++++++++++++++++++++++++++++ + server/object.h | 4 + + 5 files changed, 331 insertions(+), 2 deletions(-) + create mode 100644 server/fast_sync.c + +diff --git a/configure.ac b/configure.ac +index 6cbc0a61be1..98b7746c726 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -443,6 +443,7 @@ AC_CHECK_HEADERS(\ + linux/serial.h \ + linux/types.h \ + linux/ucdrom.h \ ++ linux/winesync.h \ + lwp.h \ + mach-o/loader.h \ + mach/mach.h \ +diff --git a/server/Makefile.in b/server/Makefile.in +index 739d0517339..919e652eceb 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ C_SRCS = \ + directory.c \ + esync.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + fsync.c \ +diff --git a/server/event.c b/server/event.c +index d2ed6ae3df7..8c82f8445c4 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -56,6 +56,7 @@ struct event + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -63,6 +64,7 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *event_get_fast_sync( struct object *obj ); + static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = +@@ -84,7 +87,7 @@ static const struct object_ops event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + event_destroy /* destroy */ + }; +@@ -152,6 +155,8 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); ++ ++ event->fast_sync = NULL; + } + } + return event; +@@ -175,6 +179,7 @@ void set_event( struct event *event ) + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); ++ fast_set_event( event->fast_sync ); + } + + void reset_event( struct event *event ) +@@ -197,6 +202,8 @@ void reset_event( struct event *event ) + + if (do_esync()) + esync_clear( event->esync_fd ); ++ ++ fast_reset_event( event->fast_sync ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -225,12 +231,27 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static struct fast_sync *event_get_fast_sync( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (!event->fast_sync) ++ { ++ enum fast_sync_type type = event->manual_reset ? FAST_SYNC_MANUAL_EVENT : FAST_SYNC_AUTO_EVENT; ++ event->fast_sync = fast_create_event( type, event->signaled ); ++ } ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ + static void event_destroy( struct object *obj ) + { + struct event *event = (struct event *)obj; + + if (do_esync()) + close( event->esync_fd ); ++ ++ if (event->fast_sync) release_object( event->fast_sync ); + } + + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..cbf14bf8081 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,302 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Zebediah Figura 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "winternl.h" ++ ++#include "file.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct linux_device ++{ ++ struct object obj; /* object header */ ++ struct fd *fd; /* fd for unix fd */ ++}; ++ ++static struct linux_device *linux_device_object; ++ ++static void linux_device_dump( struct object *obj, int verbose ); ++static struct fd *linux_device_get_fd( struct object *obj ); ++static void linux_device_destroy( struct object *obj ); ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ); ++ ++static const struct object_ops linux_device_ops = ++{ ++ sizeof(struct linux_device), /* size */ ++ &no_type, /* type */ ++ linux_device_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ linux_device_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_device_destroy /* destroy */ ++}; ++ ++static const struct fd_ops linux_device_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ linux_device_get_fd_type, /* get_fd_type */ ++ no_fd_read, /* read */ ++ no_fd_write, /* write */ ++ no_fd_flush, /* flush */ ++ no_fd_get_file_info, /* get_file_info */ ++ no_fd_get_volume_info, /* get_volume_info */ ++ no_fd_ioctl, /* ioctl */ ++ default_fd_cancel_async, /* cancel_async */ ++ no_fd_queue_async, /* queue_async */ ++ default_fd_reselect_async /* reselect_async */ ++}; ++ ++static void linux_device_dump( struct object *obj, int verbose ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ fprintf( stderr, "Fast synchronization device fd=%p\n", device->fd ); ++} ++ ++static struct fd *linux_device_get_fd( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ return (struct fd *)grab_object( device->fd ); ++} ++ ++static void linux_device_destroy( struct object *obj ) ++{ ++ struct linux_device *device = (struct linux_device *)obj; ++ assert( obj->ops == &linux_device_ops ); ++ if (device->fd) release_object( device->fd ); ++ linux_device_object = NULL; ++} ++ ++static enum server_fd_type linux_device_get_fd_type( struct fd *fd ) ++{ ++ return FD_TYPE_FILE; ++} ++ ++static struct linux_device *get_linux_device(void) ++{ ++ struct linux_device *device; ++ int unix_fd; ++ ++ if (linux_device_object) ++ return (struct linux_device *)grab_object( linux_device_object ); ++ ++ unix_fd = open( "/dev/winesync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ fprintf( stderr, "fastsync: /dev/winesync not available. (Please check winesync module)\n" ); ++ return NULL; ++ } ++ ++ if (!(device = alloc_object( &linux_device_ops ))) ++ { ++ close( unix_fd ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ if (!(device->fd = create_anonymous_fd( &linux_device_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ set_error( STATUS_NO_MEMORY ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ struct linux_device *device; ++ enum fast_sync_type type; ++ unsigned int linux_obj; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++ ++static const struct object_ops fast_sync_ops = ++{ ++ sizeof(struct fast_sync), /* size */ ++ &no_type, /* type */ ++ linux_obj_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* satisfied */ ++ NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ default_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ no_link_name, /* link_name */ ++ NULL, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_get_fast_sync, /* get_fast_sync */ ++ no_close_handle, /* close_handle */ ++ linux_obj_destroy /* destroy */ ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &fast_sync_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u linux_obj=%u\n", ++ fast_sync->type, fast_sync->linux_obj ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_DELETE, &fast_sync->linux_obj ); ++ release_object( fast_sync->device ); ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct winesync_event_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.signaled = signaled; ++ switch (type) ++ { ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_AUTO_SERVER: ++ args.manual = 0; ++ break; ++ ++ case FAST_SYNC_MANUAL_EVENT: ++ case FAST_SYNC_MANUAL_SERVER: ++ case FAST_SYNC_QUEUE: ++ args.manual = 1; ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ case FAST_SYNC_SEMAPHORE: ++ assert(0); ++ break; ++ } ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = type; ++ fast_sync->linux_obj = args.event; ++ ++ return fast_sync; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_SET_EVENT, &args ); ++} ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ struct winesync_event_args args = {0}; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_reset_event %u\n", fast_sync->linux_obj ); ++ ++ args.event = fast_sync->linux_obj; ++ ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++#endif +diff --git a/server/object.h b/server/object.h +index 89ca434bd62..eac36a346d3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -224,6 +224,10 @@ extern void abandon_mutexes( struct thread *thread ); + + /* fast-synchronization functions */ + ++extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern void fast_set_event( struct fast_sync *obj ); ++extern void fast_reset_event( struct fast_sync *obj ); ++ + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + + /* serial functions */ +-- +2.35.3 + +From e9a8818fc551de512316e6b9587ba4270426b39d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 15:10:38 -0500 +Subject: [PATCH 03/30] autoreconf + +--- + configure | 6 ++++++ + include/config.h.in | 3 +++ + 2 files changed, 9 insertions(+) + +diff --git a/configure b/configure +index 3ac0a8233fb..2fe39de5c20 100755 +--- a/configure ++++ b/configure +@@ -8011,6 +8011,12 @@ if test "x$ac_cv_header_linux_ucdrom_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_UCDROM_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/winesync.h" "ac_cv_header_linux_winesync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_winesync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_WINESYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "lwp.h" "ac_cv_header_lwp_h" "$ac_includes_default" + if test "x$ac_cv_header_lwp_h" = xyes +diff --git a/include/config.h.in b/include/config.h.in +index 45a0bd07abb..69a1766afaf 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -227,6 +227,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_VIDEODEV2_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_WINESYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LWP_H + +-- +2.35.3 + +From 0dc438a8c56af512eea748ab4a6988e0cc2f498f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:45:30 -0600 +Subject: [PATCH 04/30] server: Create fast synchronization objects for + semaphores. + +--- + server/fast_sync.c | 33 +++++++++++++++++++++++++++++++++ + server/object.h | 1 + + server/semaphore.c | 25 +++++++++++++++++++++++-- + 3 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index cbf14bf8081..093b42cb601 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -248,6 +248,33 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return fast_sync; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct winesync_sem_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_SEMAPHORE; ++ fast_sync->linux_obj = args.sem; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -286,6 +313,12 @@ struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) + return NULL; + } + ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +diff --git a/server/object.h b/server/object.h +index eac36a346d3..09f7828ba9c 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -225,6 +225,7 @@ extern void abandon_mutexes( struct thread *thread ); + /* fast-synchronization functions */ + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); ++extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); + +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a89bd0886b..99409198d68 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -55,12 +55,15 @@ struct semaphore + struct object obj; /* object header */ + unsigned int count; /* current count */ + unsigned int max; /* maximum possible count */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void semaphore_dump( struct object *obj, int verbose ); + static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int semaphore_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ); ++static void semaphore_destroy( struct object *obj ); + + static const struct object_ops semaphore_ops = + { +@@ -82,9 +85,9 @@ static const struct object_ops semaphore_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni + /* initialize it if it didn't already exist */ + sem->count = initial; + sem->max = max; ++ sem->fast_sync = NULL; + } + } + return sem; +@@ -168,6 +172,23 @@ static int semaphore_signal( struct object *obj, unsigned int access ) + return release_semaphore( sem, 1, NULL ); + } + ++static struct fast_sync *semaphore_get_fast_sync( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (!semaphore->fast_sync) ++ semaphore->fast_sync = fast_create_semaphore( semaphore->count, semaphore->max ); ++ if (semaphore->fast_sync) grab_object( semaphore->fast_sync ); ++ return semaphore->fast_sync; ++} ++ ++static void semaphore_destroy( struct object *obj ) ++{ ++ struct semaphore *semaphore = (struct semaphore *)obj; ++ ++ if (semaphore->fast_sync) release_object( semaphore->fast_sync ); ++} ++ + /* create a semaphore */ + DECL_HANDLER(create_semaphore) + { +-- +2.35.3 + +From 8d3bb97167efa64a5e3dd9a60d93506154f3f8e2 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 16:52:55 -0600 +Subject: [PATCH 05/30] server: Create fast synchronization objects for + mutexes. + +--- + server/fast_sync.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ + server/mutex.c | 24 ++++++++++++++++++---- + server/object.h | 2 ++ + server/thread.c | 1 + + 4 files changed, 74 insertions(+), 4 deletions(-) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 093b42cb601..fe366a4f8ec 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -275,6 +275,33 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return fast_sync; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ struct linux_device *device; ++ struct fast_sync *fast_sync; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ if (!(fast_sync = alloc_object( &fast_sync_ops ))) return NULL; ++ ++ /* transfer our device reference to the fast sync object */ ++ fast_sync->device = device; ++ fast_sync->type = FAST_SYNC_MUTEX; ++ fast_sync->linux_obj = args.mutex; ++ ++ return fast_sync; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + struct winesync_event_args args = {0}; +@@ -305,6 +332,20 @@ void fast_reset_event( struct fast_sync *fast_sync ) + ioctl( get_unix_fd( fast_sync->device->fd ), WINESYNC_IOC_RESET_EVENT, &args ); + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) ++ { ++ clear_error(); ++ return; ++ } ++ ++ ioctl( get_unix_fd( device->fd ), WINESYNC_IOC_KILL_OWNER, &tid ); ++ release_object( device ); ++} ++ + #else + + struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) +@@ -319,6 +360,12 @@ struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) + return NULL; + } + ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ + void fast_set_event( struct fast_sync *fast_sync ) + { + } +@@ -327,4 +374,8 @@ void fast_reset_event( struct fast_sync *obj ) + { + } + ++void fast_abandon_mutexes( thread_id_t tid ) ++{ ++} ++ + #endif +diff --git a/server/mutex.c b/server/mutex.c +index f7ad1e800c9..0311c133f40 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -57,6 +57,7 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +65,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void mutex_destroy( struct object *obj ); + static int mutex_signal( struct object *obj, unsigned int access ); ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ); + + static const struct object_ops mutex_ops = + { +@@ -85,7 +87,7 @@ static const struct object_ops mutex_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -128,6 +130,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + mutex->owner = NULL; + mutex->abandoned = 0; + if (owned) do_grab( mutex, current ); ++ mutex->fast_sync = NULL; + } + } + return mutex; +@@ -190,14 +193,27 @@ static int mutex_signal( struct object *obj, unsigned int access ) + return 1; + } + ++static struct fast_sync *mutex_get_fast_sync( struct object *obj ) ++{ ++ struct mutex *mutex = (struct mutex *)obj; ++ ++ if (!mutex->fast_sync) ++ mutex->fast_sync = fast_create_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count ); ++ if (mutex->fast_sync) grab_object( mutex->fast_sync ); ++ return mutex->fast_sync; ++} ++ + static void mutex_destroy( struct object *obj ) + { + struct mutex *mutex = (struct mutex *)obj; + assert( obj->ops == &mutex_ops ); + +- if (!mutex->count) return; +- mutex->count = 0; +- do_release( mutex ); ++ if (mutex->count) ++ { ++ mutex->count = 0; ++ do_release( mutex ); ++ } ++ if (mutex->fast_sync) release_object( mutex->fast_sync ); + } + + /* create a mutex */ +diff --git a/server/object.h b/server/object.h +index 09f7828ba9c..8401a1ffed7 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -226,8 +226,10 @@ extern void abandon_mutexes( struct thread *thread ); + + extern struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ); + extern struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ); ++extern struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ); + extern void fast_set_event( struct fast_sync *obj ); + extern void fast_reset_event( struct fast_sync *obj ); ++extern void fast_abandon_mutexes( thread_id_t tid ); + + extern struct fast_sync *no_get_fast_sync( struct object *obj ); + +diff --git a/server/thread.c b/server/thread.c +index 5b78bbc3ff9..1f7e9e6de3a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -1291,6 +1291,7 @@ void kill_thread( struct thread *thread, int violent_death ) + fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); ++ fast_abandon_mutexes( thread->id ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +-- +2.35.3 + +From 76073026dbeca2a2f473cb349b7ef8a676b9e28d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 11 Mar 2021 20:32:58 -0600 +Subject: [PATCH 06/30] server: Create fast synchronization objects for + completion ports. + +--- + server/completion.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/server/completion.c b/server/completion.c +index dd16787c63c..5ec6d209d13 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -61,11 +61,13 @@ struct completion + struct list queue; + unsigned int depth; + int abandoned; ++ struct fast_sync *fast_sync; + }; + + static void completion_dump( struct object*, int ); + static int completion_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int completion_close( struct object *obj, struct process *process, obj_handle_t handle ); ++static struct fast_sync *completion_get_fast_sync( struct object *obj ); + static void completion_destroy( struct object * ); + + static const struct object_ops completion_ops = +@@ -87,7 +89,7 @@ static const struct object_ops completion_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ completion_get_fast_sync, /* get_fast_sync */ + completion_close, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -110,6 +112,7 @@ static void completion_destroy( struct object *obj) + { + free( tmp ); + } ++ if (completion->fast_sync) release_object( completion->fast_sync ); + } + + static void completion_dump( struct object *obj, int verbose ) +@@ -127,6 +130,16 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent + return !list_empty( &completion->queue ); + } + ++static struct fast_sync *completion_get_fast_sync( struct object *obj ) ++{ ++ struct completion *completion = (struct completion *)obj; ++ ++ if (!completion->fast_sync) ++ completion->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) ); ++ if (completion->fast_sync) grab_object( completion->fast_sync ); ++ return completion->fast_sync; ++} ++ + static struct completion *create_completion( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int concurrent, + const struct security_descriptor *sd ) +@@ -139,6 +152,7 @@ static struct completion *create_completion( struct object *root, const struct u + { + list_init( &completion->queue ); + completion->depth = 0; ++ completion->fast_sync = NULL; + } + } + +@@ -166,6 +180,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ + list_add_tail( &completion->queue, &msg->queue_entry ); + completion->depth++; + wake_up( &completion->obj, 1 ); ++ fast_set_event( completion->fast_sync ); + } + + /* create a completion */ +@@ -232,6 +247,8 @@ DECL_HANDLER(remove_completion) + reply->status = msg->status; + reply->information = msg->information; + free( msg ); ++ if (list_empty( &completion->queue )) ++ fast_reset_event( completion->fast_sync ); + } + + release_object( completion ); +-- +2.35.3 + +From 514d866f983ad0444b9cbc5769aba28e42c4fd0e Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 16:54:34 -0600 +Subject: [PATCH 07/30] server: Create fast synchronization objects for + consoles. + +--- + server/console.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/server/console.c b/server/console.c +index 2c62f7518d5..662e6312554 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -61,6 +61,7 @@ struct console + struct fd *fd; /* for bare console, attached input fd */ + struct async_queue ioctl_q; /* ioctl queue */ + struct async_queue read_q; /* read queue */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_dump( struct object *obj, int verbose ); +@@ -72,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st + static struct object *console_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_add_queue( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *console_get_fast_sync( struct object *obj ); + + static const struct object_ops console_ops = + { +@@ -93,7 +95,7 @@ static const struct object_ops console_ops = + NULL, /* unlink_name */ + console_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -220,6 +222,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *screen_buffer_get_fd( struct object *obj ); + static struct object *screen_buffer_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ); + + static const struct object_ops screen_buffer_ops = + { +@@ -241,7 +244,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* unlink_name */ + screen_buffer_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -307,6 +310,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + unsigned int sharing, unsigned int options ); + static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *console_input_get_fd( struct object *obj ); ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ); + static void console_input_destroy( struct object *obj ); + + static const struct object_ops console_input_ops = +@@ -329,7 +333,7 @@ static const struct object_ops console_input_ops = + default_unlink_name, /* unlink_name */ + console_input_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -365,6 +369,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry + static struct fd *console_output_get_fd( struct object *obj ); + static struct object *console_output_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ); + static void console_output_destroy( struct object *obj ); + + static const struct object_ops console_output_ops = +@@ -387,7 +392,7 @@ static const struct object_ops console_output_ops = + default_unlink_name, /* unlink_name */ + console_output_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -546,6 +551,7 @@ static struct object *create_console(void) + console->server = NULL; + console->fd = NULL; + console->last_id = 0; ++ console->fast_sync = NULL; + init_async_queue( &console->ioctl_q ); + init_async_queue( &console->read_q ); + +@@ -769,6 +775,8 @@ static void console_destroy( struct object *obj ) + free_async_queue( &console->read_q ); + if (console->fd) + release_object( console->fd ); ++ ++ if (console->fast_sync) release_object( console->fast_sync ); + } + + static struct object *create_console_connection( struct console *console ) +@@ -816,6 +824,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access + return grab_object( obj ); + } + ++static struct fast_sync *console_get_fast_sync( struct object *obj ) ++{ ++ struct console *console = (struct console *)obj; ++ ++ if (!console->fast_sync) ++ console->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, console->signaled ); ++ if (console->fast_sync) grab_object( console->fast_sync ); ++ return console->fast_sync; ++} ++ + static void screen_buffer_dump( struct object *obj, int verbose ) + { + struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; +@@ -865,6 +883,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj ) + return NULL; + } + ++static struct fast_sync *screen_buffer_get_fast_sync( struct object *obj ) ++{ ++ struct screen_buffer *screen_buffer = (struct screen_buffer *)obj; ++ if (!screen_buffer->input) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( &screen_buffer->input->obj ); ++} ++ + static void console_server_dump( struct object *obj, int verbose ) + { + assert( obj->ops == &console_server_ops ); +@@ -1407,6 +1436,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_input_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_input_destroy( struct object *obj ) + { + struct console_input *console_input = (struct console_input *)obj; +@@ -1479,6 +1518,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_output_get_fast_sync( struct object *obj ) ++{ ++ if (!current->process->console || !current->process->console->active) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ return NULL; ++ } ++ return console_get_fast_sync( ¤t->process->console->obj ); ++} ++ + static void console_output_destroy( struct object *obj ) + { + struct console_output *console_output = (struct console_output *)obj; +@@ -1536,11 +1585,16 @@ DECL_HANDLER(get_next_console_request) + + if (!server->console->renderer) server->console->renderer = current; + +- if (!req->signal) server->console->signaled = 0; ++ if (!req->signal) ++ { ++ server->console->signaled = 0; ++ fast_reset_event( server->console->fast_sync ); ++ } + else if (!server->console->signaled) + { + server->console->signaled = 1; + wake_up( &server->console->obj, 0 ); ++ fast_set_event( server->console->fast_sync ); + } + + if (req->read) +-- +2.35.3 + +From 66b3df905b0189890b2c23039d11231104b76339 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:00:51 -0600 +Subject: [PATCH 08/30] server: Create fast synchronization objects for console + servers. + +--- + server/console.c | 24 +++++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/server/console.c b/server/console.c +index 662e6312554..96c63db3328 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -142,6 +142,7 @@ struct console_server + struct termios termios; /* original termios */ + int esync_fd; + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -152,6 +153,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + unsigned int attr, struct object *root ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ); + + static const struct object_ops console_server_ops = + { +@@ -173,7 +175,7 @@ static const struct object_ops console_server_ops = + NULL, /* unlink_name */ + console_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -591,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u + } + list_add_tail( &server->queue, &ioctl->entry ); + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + if (async) set_error( STATUS_PENDING ); + return 1; + } +@@ -623,6 +626,7 @@ static void disconnect_console_server( struct console_server *server ) + server->console->server = NULL; + server->console = NULL; + wake_up( &server->obj, 0 ); ++ fast_set_event( server->fast_sync ); + } + } + +@@ -906,6 +910,7 @@ static void console_server_destroy( struct object *obj ) + disconnect_console_server( server ); + if (server->fd) release_object( server->fd ); + if (do_esync()) close( server->esync_fd ); ++ if (server->fast_sync) release_object( server->fast_sync ); + } + + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, +@@ -960,6 +965,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int + return grab_object( obj ); + } + ++static struct fast_sync *console_server_get_fast_sync( struct object *obj ) ++{ ++ struct console_server *server = (struct console_server *)obj; ++ int signaled = !server->console || !list_empty( &server->queue ); ++ ++ if (!server->fast_sync) ++ server->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (server->fast_sync) grab_object( server->fast_sync ); ++ return server->fast_sync; ++} ++ + static struct object *create_console_server( void ) + { + struct console_server *server; +@@ -971,6 +987,7 @@ static struct object *create_console_server( void ) + server->term_fd = -1; + list_init( &server->queue ); + list_init( &server->read_queue ); ++ server->fast_sync = NULL; + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); + if (!server->fd) + { +@@ -1616,6 +1633,9 @@ DECL_HANDLER(get_next_console_request) + + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); ++ ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); + } + + if (ioctl) +@@ -1702,5 +1721,8 @@ DECL_HANDLER(get_next_console_request) + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + ++ if (list_empty( &server->queue )) ++ fast_reset_event( server->fast_sync ); ++ + release_object( server ); + } +-- +2.35.3 + +From a78f738d6b7599aa5ffff27f9914834914b7b718 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:11:03 -0600 +Subject: [PATCH 09/30] server: Create fast synchronization objects for debug + objects. + +--- + server/debugger.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/server/debugger.c b/server/debugger.c +index 80c2b5389c6..04172b7e66d 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -71,6 +71,7 @@ struct debug_obj + struct object obj; /* object header */ + struct list event_queue; /* pending events queue */ + unsigned int flags; /* debug flags */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + +@@ -105,6 +106,7 @@ static const struct object_ops debug_event_ops = + + static void debug_obj_dump( struct object *obj, int verbose ); + static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ); + static void debug_obj_destroy( struct object *obj ); + + static const struct object_ops debug_obj_ops = +@@ -127,7 +129,7 @@ static const struct object_ops debug_obj_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event ) + /* grab reference since debugger could be killed while trying to wake up */ + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event + { + grab_object( debug_obj ); + wake_up( &debug_obj->obj, 0 ); ++ fast_set_event( debug_obj->fast_sync ); + release_object( debug_obj ); + } + } +@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr + return find_event_to_send( debug_obj ) != NULL; + } + ++static struct fast_sync *debug_obj_get_fast_sync( struct object *obj ) ++{ ++ struct debug_obj *debug_obj = (struct debug_obj *)obj; ++ int signaled = find_event_to_send( debug_obj ) != NULL; ++ ++ if (!debug_obj->fast_sync) ++ debug_obj->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, signaled ); ++ if (debug_obj->fast_sync) grab_object( debug_obj->fast_sync ); ++ return debug_obj->fast_sync; ++} ++ + static void debug_obj_destroy( struct object *obj ) + { + struct list *ptr; +@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj ) + /* free all pending events */ + while ((ptr = list_head( &debug_obj->event_queue ))) + unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry )); ++ ++ if (debug_obj->fast_sync) release_object( debug_obj->fast_sync ); + } + + struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access ) +@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni + { + debug_obj->flags = flags; + list_init( &debug_obj->event_queue ); ++ debug_obj->fast_sync = NULL; + } + } + return debug_obj; +@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event) + reply->tid = get_thread_id( event->sender ); + alloc_event_handles( event, current->process ); + set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); ++ ++ if (!find_event_to_send( debug_obj )) ++ fast_reset_event( debug_obj->fast_sync ); + } + else + { +-- +2.35.3 + +From af11ddd1e4d4fa0c3d17ab8a3ccf490d0f492bf4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 10 Mar 2021 11:02:42 -0600 +Subject: [PATCH 10/30] server: Create fast synchronization objects for device + managers. + +--- + server/device.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/server/device.c b/server/device.c +index 691c0eb6b5f..e718263ebb5 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -93,12 +93,14 @@ struct device_manager + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -119,7 +121,7 @@ static const struct object_ops device_manager_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -421,7 +423,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i + irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL; + if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry ); + list_add_tail( &manager->requests, &irp->mgr_entry ); +- if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 ); /* first one */ ++ if (list_head( &manager->requests ) == &irp->mgr_entry) ++ { ++ /* first one */ ++ wake_up( &manager->obj, 0 ); ++ fast_set_event( manager->fast_sync ); ++ } + } + + static struct object *device_open_file( struct object *obj, unsigned int access, +@@ -755,6 +762,9 @@ static void delete_file( struct device_file *file ) + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } + ++ if (list_empty( &file->device->manager->requests )) ++ fast_reset_event( file->device->manager->fast_sync ); ++ + release_object( file ); + } + +@@ -786,6 +796,16 @@ static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync + return manager->fsync_idx; + } + ++static struct fast_sync *device_manager_get_fast_sync( struct object *obj ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ ++ if (!manager->fast_sync) ++ manager->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) ); ++ if (manager->fast_sync) grab_object( manager->fast_sync ); ++ return manager->fast_sync; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -820,6 +840,8 @@ static void device_manager_destroy( struct object *obj ) + + if (do_esync()) + close( manager->esync_fd ); ++ ++ if (manager->fast_sync) release_object( manager->fast_sync ); + } + + static struct device_manager *create_device_manager(void) +@@ -829,6 +851,7 @@ static struct device_manager *create_device_manager(void) + if ((manager = alloc_object( &device_manager_ops ))) + { + manager->current_call = NULL; ++ manager->fast_sync = NULL; + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); +@@ -1018,6 +1041,10 @@ DECL_HANDLER(get_next_device_request) + } + list_remove( &irp->mgr_entry ); + list_init( &irp->mgr_entry ); ++ ++ if (list_empty( &manager->requests )) ++ fast_reset_event( manager->fast_sync ); ++ + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; +-- +2.35.3 + +From 853da9bcd3d80a468f4ac1bcd9dd6ae7d98ade9c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:13:20 -0600 +Subject: [PATCH 11/30] server: Create fast synchronization objects for keyed + events. + +--- + server/event.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/server/event.c b/server/event.c +index 8c82f8445c4..b750a22487b 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -110,10 +110,13 @@ struct type_descr keyed_event_type = + struct keyed_event + { + struct object obj; /* object header */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void keyed_event_dump( struct object *obj, int verbose ); + static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ); ++static void keyed_event_destroy( struct object *obj ); + + static const struct object_ops keyed_event_ops = + { +@@ -135,9 +138,9 @@ static const struct object_ops keyed_event_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -261,6 +264,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + /* initialize it if it didn't already exist */ ++ event->fast_sync = NULL; + } + } + return event; +@@ -304,6 +308,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en + return 0; + } + ++static struct fast_sync *keyed_event_get_fast_sync( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (!event->fast_sync) ++ event->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, 1 ); ++ if (event->fast_sync) grab_object( event->fast_sync ); ++ return event->fast_sync; ++} ++ ++static void keyed_event_destroy( struct object *obj ) ++{ ++ struct keyed_event *event = (struct keyed_event *)obj; ++ ++ if (event->fast_sync) release_object( event->fast_sync ); ++} ++ + /* create an event */ + DECL_HANDLER(create_event) + { +-- +2.35.3 + +From 9c6cba6f52ed5029b841f5ee5948aa48597fed49 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:16:53 -0600 +Subject: [PATCH 12/30] server: Create fast synchronization objects for + processes. + +--- + server/process.c | 17 ++++++++++++++++- + server/process.h | 1 + + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index 4d146dd79fc..b29fb2bc983 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -94,6 +94,7 @@ static unsigned int process_map_access( struct object *obj, unsigned int access + static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *process_get_fast_sync( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); +@@ -117,7 +118,7 @@ static const struct object_ops process_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + process_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -683,6 +684,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->esync_fd = -1; + process->fsync_idx = 0; + process->cpu_override.cpu_count = 0; ++ process->fast_sync = NULL; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -787,6 +789,8 @@ static void process_destroy( struct object *obj ) + free( process->dir_cache ); + free( process->image ); + if (do_esync()) close( process->esync_fd ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -818,6 +822,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj ) + return &process->kernel_object; + } + ++static struct fast_sync *process_get_fast_sync( struct object *obj ) ++{ ++ struct process *process = (struct process *)obj; ++ ++ if (!process->fast_sync) ++ process->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, !process->running_threads ); ++ if (process->fast_sync) grab_object( process->fast_sync ); ++ return process->fast_sync; ++} ++ + static struct security_descriptor *process_get_sd( struct object *obj ) + { + static struct security_descriptor *process_default_sd; +@@ -990,6 +1004,7 @@ static void process_killed( struct process *process ) + release_job_process( process ); + start_sigkill_timer( process ); + wake_up( &process->obj, 0 ); ++ fast_set_event( process->fast_sync ); + } + + /* add a thread to a process running threads list */ +diff --git a/server/process.h b/server/process.h +index 632faf9c4bf..5b8bebd31be 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -90,6 +90,7 @@ struct process + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; + struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + /* process functions */ +-- +2.35.3 + +From 54f773cbc0ae65d5cd90f5fb18a8f74415742102 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:17:35 -0600 +Subject: [PATCH 13/30] server: Create fast synchronization objects for jobs. + +--- + server/process.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/server/process.c b/server/process.c +index b29fb2bc983..2ad6a3232ea 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -193,6 +193,7 @@ struct type_descr job_type = + + static void job_dump( struct object *obj, int verbose ); + static int job_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *job_get_fast_sync( struct object *obj ); + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void job_destroy( struct object *obj ); + +@@ -210,6 +211,7 @@ struct job + struct job *parent; + struct list parent_job_entry; /* list entry for parent job */ + struct list child_job_list; /* list of child jobs */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static const struct object_ops job_ops = +@@ -232,7 +234,7 @@ static const struct object_ops job_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -257,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_ + job->completion_port = NULL; + job->completion_key = 0; + job->parent = NULL; ++ job->fast_sync = NULL; + } + } + return job; +@@ -413,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code ) + job->terminating = 0; + job->signaled = 1; + wake_up( &job->obj, 0 ); ++ fast_set_event( job->fast_sync ); ++} ++ ++static struct fast_sync *job_get_fast_sync( struct object *obj ) ++{ ++ struct job *job = (struct job *)obj; ++ ++ if (!job->fast_sync) ++ job->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, job->signaled ); ++ if (job->fast_sync) grab_object( job->fast_sync ); ++ return job->fast_sync; + } + + static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +@@ -443,6 +457,8 @@ static void job_destroy( struct object *obj ) + list_remove( &job->parent_job_entry ); + release_object( job->parent ); + } ++ ++ if (job->fast_sync) release_object( job->fast_sync ); + } + + static void job_dump( struct object *obj, int verbose ) +-- +2.35.3 + +From 22b3058bea7c69801f20b0c5ae48444d35b138b9 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:24:15 -0600 +Subject: [PATCH 14/30] server: Create fast synchronization objects for message + queues. + +--- + server/queue.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 44 insertions(+), 4 deletions(-) + +diff --git a/server/queue.c b/server/queue.c +index e013f4fb32c..3e70cd92259 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -138,6 +138,7 @@ struct msg_queue + int esync_in_msgwait; /* our thread is currently waiting on us */ + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + struct hotkey +@@ -155,6 +156,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); + static void thread_input_dump( struct object *obj, int verbose ); +@@ -181,7 +183,7 @@ static const struct object_ops msg_queue_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -305,6 +307,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->esync_in_msgwait = 0; + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; ++ queue->fast_sync = NULL; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -480,7 +483,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits ) + } + queue->wake_bits |= bits; + queue->changed_bits |= bits; +- if (is_signaled( queue )) wake_up( &queue->obj, 0 ); ++ if (is_signaled( queue )) ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } + } + + /* clear some queue bits */ +@@ -488,6 +495,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + + /* check whether msg is a keyboard message */ +@@ -997,6 +1006,17 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en + struct msg_queue *queue = (struct msg_queue *)obj; + queue->wake_mask = 0; + queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++} ++ ++static struct fast_sync *msg_queue_get_fast_sync( struct object *obj ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ ++ if (!queue->fast_sync) ++ queue->fast_sync = fast_create_event( FAST_SYNC_QUEUE, is_signaled( queue ) ); ++ if (queue->fast_sync) grab_object( queue->fast_sync ); ++ return queue->fast_sync; + } + + static void msg_queue_destroy( struct object *obj ) +@@ -1035,6 +1055,7 @@ static void msg_queue_destroy( struct object *obj ) + if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); + if (do_esync()) close( queue->esync_fd ); ++ if (queue->fast_sync) release_object( queue->fast_sync ); + } + + static void msg_queue_poll_event( struct fd *fd, int event ) +@@ -1045,6 +1066,7 @@ static void msg_queue_poll_event( struct fd *fd, int event ) + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + else set_fd_events( queue->fd, 0 ); + wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); + } + + static void thread_input_dump( struct object *obj, int verbose ) +@@ -2425,8 +2447,20 @@ DECL_HANDLER(set_queue_mask) + if (is_signaled( queue )) + { + /* if skip wait is set, do what would have been done in the subsequent wait */ +- if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; +- else wake_up( &queue->obj, 0 ); ++ if (req->skip_wait) ++ { ++ queue->wake_mask = queue->changed_mask = 0; ++ fast_reset_event( queue->fast_sync ); ++ } ++ else ++ { ++ wake_up( &queue->obj, 0 ); ++ fast_set_event( queue->fast_sync ); ++ } ++ } ++ else ++ { ++ fast_reset_event( queue->fast_sync ); + } + + if (do_esync() && !is_signaled( queue )) +@@ -2441,6 +2475,9 @@ DECL_HANDLER(get_queue_status) + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); ++ ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -2624,6 +2660,9 @@ DECL_HANDLER(get_message) + if (filter & QS_INPUT) queue->changed_bits &= ~QS_INPUT; + if (filter & QS_PAINT) queue->changed_bits &= ~QS_PAINT; + ++ if (!is_signaled( queue )) ++ fast_reset_event( queue->fast_sync ); ++ + /* then check for posted messages */ + if ((filter & QS_POSTMESSAGE) && + get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply )) +@@ -2677,6 +2716,7 @@ DECL_HANDLER(get_message) + if (get_win == -1 && current->process->idle_event) set_event( current->process->idle_event ); + queue->wake_mask = req->wake_mask; + queue->changed_mask = req->changed_mask; ++ fast_reset_event( queue->fast_sync ); + set_error( STATUS_PENDING ); /* FIXME */ + + if (do_esync() && !is_signaled( queue )) +-- +2.35.3 + +From 0091f60042239a557925ddb43be7b8f2c676cc70 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:26:35 -0600 +Subject: [PATCH 15/30] server: Create fast synchronization objects for + threads. + +--- + server/thread.c | 16 +++++++++++++++- + server/thread.h | 1 + + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/server/thread.c b/server/thread.c +index 1f7e9e6de3a..bbcc8fe612a 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -182,6 +182,7 @@ static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *t + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); ++static struct fast_sync *thread_get_fast_sync( struct object *obj ); + static void destroy_thread( struct object *obj ); + + static const struct object_ops thread_ops = +@@ -204,7 +205,7 @@ static const struct object_ops thread_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + thread_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -252,6 +253,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc = NULL; + thread->desc_len = 0; + thread->exit_poll = NULL; ++ thread->fast_sync = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -403,6 +405,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj ) + return &thread->kernel_object; + } + ++static struct fast_sync *thread_get_fast_sync( struct object *obj ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ ++ if (!thread->fast_sync) ++ thread->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, thread->state == TERMINATED ); ++ if (thread->fast_sync) grab_object( thread->fast_sync ); ++ return thread->fast_sync; ++} ++ + /* cleanup everything that is no longer needed by a dead thread */ + /* used by destroy_thread and kill_thread */ + static void cleanup_thread( struct thread *thread ) +@@ -457,6 +469,8 @@ static void destroy_thread( struct object *obj ) + + if (do_esync()) + close( thread->esync_fd ); ++ ++ if (thread->fast_sync) release_object( thread->fast_sync ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1293,7 +1306,11 @@ void kill_thread( struct thread *thread, int violent_death ) + send_thread_signal( thread, SIGQUIT ); + check_terminated( thread ); + } +- else wake_up( &thread->obj, 0 ); ++ else ++ { ++ wake_up( &thread->obj, 0 ); ++ fast_set_event( thread->fast_sync ); ++ } + cleanup_thread( thread ); + remove_process_thread( thread->process, thread ); + release_object( thread ); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..caabbe5bfd6 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,7 @@ struct thread + data_size_t desc_len; /* thread description length in bytes */ + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 40c41d18f6c50e9240ed691217ec33cf729e50e7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:29:38 -0600 +Subject: [PATCH 16/30] server: Create fast synchronization objects for timers. + +--- + server/timer.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/server/timer.c b/server/timer.c +index d4a69bf8794..854a8e1f7f2 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -61,6 +61,7 @@ struct timer + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void timer_dump( struct object *obj, int verbose ); +@@ -68,6 +69,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static struct fast_sync *timer_get_fast_sync( struct object *obj ); + static void timer_destroy( struct object *obj ); + + static const struct object_ops timer_ops = +@@ -88,7 +90,7 @@ static const struct object_ops timer_ops = + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -111,6 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->timeout = NULL; + timer->thread = NULL; + timer->esync_fd = -1; ++ timer->fast_sync = NULL; + + if (do_fsync()) + timer->fsync_idx = fsync_alloc_shm( 0, 0 ); +@@ -152,6 +155,7 @@ static void timer_callback( void *private ) + /* wake up waiters */ + timer->signaled = 1; + wake_up( &timer->obj, 0 ); ++ fast_set_event( timer->fast_sync ); + } + + /* cancel a running timer */ +@@ -182,6 +186,8 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + + if (do_esync()) + esync_clear( timer->esync_fd ); ++ ++ fast_reset_event( timer->fast_sync ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -216,6 +221,19 @@ static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry + if (!timer->manual) timer->signaled = 0; + } + ++static struct fast_sync *timer_get_fast_sync( struct object *obj ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ ++ if (!timer->fast_sync) ++ { ++ enum fast_sync_type type = timer->manual ? FAST_SYNC_MANUAL_SERVER : FAST_SYNC_AUTO_SERVER; ++ timer->fast_sync = fast_create_event( type, timer->signaled ); ++ } ++ if (timer->fast_sync) grab_object( timer->fast_sync ); ++ return timer->fast_sync; ++} ++ + static void timer_destroy( struct object *obj ) + { + struct timer *timer = (struct timer *)obj; +@@ -223,6 +241,7 @@ static void timer_destroy( struct object *obj ) + if (timer->timeout) remove_timeout_user( timer->timeout ); + if (timer->thread) release_object( timer->thread ); + if (do_esync()) close( timer->esync_fd ); ++ if (timer->fast_sync) release_object( timer->fast_sync ); + } + + /* create a timer */ +-- +2.35.3 + +From 3ee8b55caef2e80f404c12cdb5439a14b85d045b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:40:57 -0600 +Subject: [PATCH 17/30] server: Create fast synchronization objects for + fd-based objects. + +--- + server/change.c | 2 +- + server/device.c | 2 +- + server/fd.c | 27 ++++++++++++++++++++++++++- + server/file.c | 2 +- + server/file.h | 1 + + server/mailslot.c | 4 ++-- + server/named_pipe.c | 6 +++--- + server/serial.c | 2 +- + server/sock.c | 2 +- + 9 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/server/change.c b/server/change.c +index c01dcd84b49..044529af835 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -124,7 +124,7 @@ static const struct object_ops dir_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + dir_close_handle, /* close_handle */ + dir_destroy /* destroy */ + }; +diff --git a/server/device.c b/server/device.c +index e718263ebb5..698fee63f03 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -232,7 +232,7 @@ static const struct object_ops device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + device_file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +diff --git a/server/fd.c b/server/fd.c +index 6b0693507e2..d7c2ab32a06 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -193,6 +193,7 @@ struct fd + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ + unsigned int fsync_idx; /* fsync shm index */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -1591,6 +1592,8 @@ static void fd_destroy( struct object *obj ) + + if (do_esync()) + close( fd->esync_fd ); ++ ++ if (fd->fast_sync) release_object( fd->fast_sync ); + } + + /* check if the desired access is possible without violating */ +@@ -1707,6 +1709,7 @@ static struct fd *alloc_fd_object(void) + fd->comp_flags = 0; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1745,6 +1748,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; + fd->fsync_idx = 0; ++ fd->fast_sync = NULL; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -2142,7 +2146,15 @@ void set_fd_signaled( struct fd *fd, int signaled ) + { + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; +- if (signaled) wake_up( fd->user, 0 ); ++ if (signaled) ++ { ++ wake_up( fd->user, 0 ); ++ fast_set_event( fd->fast_sync ); ++ } ++ else ++ { ++ fast_reset_event( fd->fast_sync ); ++ } + + if (do_fsync() && !signaled) + fsync_clear( fd->user ); +@@ -2168,6 +2180,19 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++struct fast_sync *default_fd_get_fast_sync( struct object *obj ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ struct fast_sync *ret; ++ ++ if (!fd->fast_sync) ++ fd->fast_sync = fast_create_event( FAST_SYNC_MANUAL_SERVER, fd->signaled ); ++ ret = fd->fast_sync; ++ release_object( fd ); ++ if (ret) grab_object( ret ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index e572b9f3c36..3120c7e67ae 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -106,7 +106,7 @@ static const struct object_ops file_ops = + NULL, /* unlink_name */ + file_open_file, /* open_file */ + file_get_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + file_destroy /* destroy */ + }; +diff --git a/server/file.h b/server/file.h +index 9f9d4cd4e1a..e6bfdea0de5 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -106,6 +106,7 @@ extern void get_nt_name( struct fd *fd, struct unicode_str *name ); + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++extern struct fast_sync *default_fd_get_fast_sync( struct object *obj ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_cancel_async( struct fd *fd, struct async *async ); +diff --git a/server/mailslot.c b/server/mailslot.c +index d58c95042a0..d9807b4ad23 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -86,7 +86,7 @@ static const struct object_ops mailslot_ops = + default_unlink_name, /* unlink_name */ + mailslot_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_destroy /* destroy */ + }; +@@ -241,7 +241,7 @@ static const struct object_ops mailslot_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mailslot_device_file_destroy /* destroy */ + }; +diff --git a/server/named_pipe.c b/server/named_pipe.c +index dec4f95c6c3..44d3de4cadd 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -180,7 +180,7 @@ static const struct object_ops pipe_server_ops = + NULL, /* unlink_name */ + pipe_server_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; +@@ -225,7 +225,7 @@ static const struct object_ops pipe_client_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + async_close_obj_handle, /* close_handle */ + pipe_end_destroy /* destroy */ + }; +@@ -305,7 +305,7 @@ static const struct object_ops named_pipe_device_file_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + named_pipe_device_file_destroy /* destroy */ + }; +diff --git a/server/serial.c b/server/serial.c +index b2a1f79a211..5c210d10a80 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -97,7 +97,7 @@ static const struct object_ops serial_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + serial_destroy /* destroy */ + }; +diff --git a/server/sock.c b/server/sock.c +index ef250e725c1..92117ab5af1 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -267,7 +267,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ +- no_get_fast_sync, /* get_fast_sync */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +-- +2.35.3 + +From 50ac049c1ce9cea8269c7d3cd97df2ba96912e0c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:46:06 -0600 +Subject: [PATCH 18/30] server: Add a request to retrieve the fast + synchronization device. + +--- + server/fast_sync.c | 17 +++++++++++++++++ + server/protocol.def | 7 +++++++ + 2 files changed, 24 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index fe366a4f8ec..bd43305b894 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -29,6 +29,8 @@ + #include "winternl.h" + + #include "file.h" ++#include "handle.h" ++#include "request.h" + #include "thread.h" + + #ifdef HAVE_LINUX_WINESYNC_H +@@ -379,3 +381,18 @@ void fast_abandon_mutexes( thread_id_t tid ) + } + + #endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct linux_device *device; ++ ++ if ((device = get_linux_device())) ++ { ++ reply->handle = alloc_handle( current->process, device, 0, 0 ); ++ release_object( device ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index ba8c0c1e363..60a39d17e20 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3758,3 +3758,10 @@ enum fast_sync_type + FAST_SYNC_MANUAL_SERVER, + FAST_SYNC_QUEUE, + }; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@REPLY ++ obj_handle_t handle; /* handle to the device */ ++@END +-- +2.35.3 + +From 7fae5a856b84061fe95079dcd945908a207cdb3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 17:55:00 -0600 +Subject: [PATCH 19/30] server: Add a request to retrieve the fast + synchronization object from a handle. + +--- + server/fast_sync.c | 24 ++++++++++++++++++++++++ + server/protocol.def | 11 +++++++++++ + 2 files changed, 35 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index bd43305b894..6b738519c7a 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -396,3 +396,27 @@ DECL_HANDLER(get_linux_sync_device) + set_error( STATUS_NOT_IMPLEMENTED ); + #endif + } ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_WINESYNC_H ++ struct object *obj; ++ ++ if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) ++ { ++ struct fast_sync *fast_sync; ++ ++ if ((fast_sync = obj->ops->get_fast_sync( obj ))) ++ { ++ reply->handle = alloc_handle( current->process, fast_sync, 0, 0 ); ++ reply->obj = fast_sync->linux_obj; ++ reply->type = fast_sync->type; ++ reply->access = get_handle_access( current->process, req->handle ); ++ release_object( fast_sync ); ++ } ++ release_object( obj ); ++ } ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++#endif ++} +diff --git a/server/protocol.def b/server/protocol.def +index 60a39d17e20..28a687949e9 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3765,3 +3765,14 @@ enum fast_sync_type + @REPLY + obj_handle_t handle; /* handle to the device */ + @END ++ ++ ++/* Get the fast synchronization object associated with the given handle */ ++@REQ(get_linux_sync_obj) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ obj_handle_t handle; /* handle to the fast synchronization object */ ++ int obj; /* linux object */ ++ int type; /* object type */ ++ unsigned int access; /* handle access rights */ ++@END +-- +2.35.3 + +From dd0e7ecf2e2274b18575718a75126999ae5fdfac Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 11:32:25 -0600 +Subject: [PATCH 20/30] server: Introduce fast_select_queue and + fast_unselect_queue requests. + +--- + server/protocol.def | 13 ++++++++++ + server/queue.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index 28a687949e9..b9342731964 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3776,3 +3776,16 @@ enum fast_sync_type + int type; /* object type */ + unsigned int access; /* handle access rights */ + @END ++ ++ ++/* Begin a client-side wait on a message queue */ ++@REQ(fast_select_queue) ++ obj_handle_t handle; /* handle to the queue */ ++@END ++ ++ ++/* End a client-side wait on a message queue */ ++@REQ(fast_unselect_queue) ++ obj_handle_t handle; /* handle to the queue */ ++ int signaled; /* was the queue signaled? */ ++@END +diff --git a/server/queue.c b/server/queue.c +index 3e70cd92259..e1fb696259f 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -139,6 +139,7 @@ struct msg_queue + unsigned int fsync_idx; + int fsync_in_msgwait; /* our thread is currently waiting on us */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -308,6 +309,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->fsync_idx = 0; + queue->fsync_in_msgwait = 0; + queue->fast_sync = NULL; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -946,6 +948,9 @@ static int is_queue_hung( struct msg_queue *queue ) + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + ++ if (queue->in_fast_wait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -3438,3 +3444,56 @@ DECL_HANDLER(fsync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fast_select_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ /* a thread can only wait on its own queue */ ++ if (current->queue != queue || queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ if (queue->fd) ++ set_fd_events( queue->fd, POLLIN ); ++ ++ queue->in_fast_wait = 1; ++ } ++ ++ release_object( queue ); ++} ++ ++DECL_HANDLER(fast_unselect_queue) ++{ ++ struct msg_queue *queue; ++ ++ if (!(queue = (struct msg_queue *)get_handle_obj( current->process, req->handle, ++ SYNCHRONIZE, &msg_queue_ops ))) ++ return; ++ ++ if (current->queue != queue || !queue->in_fast_wait) ++ { ++ set_error( STATUS_ACCESS_DENIED ); ++ } ++ else ++ { ++ if (queue->fd) ++ set_fd_events( queue->fd, 0 ); ++ ++ if (req->signaled) ++ msg_queue_satisfied( &queue->obj, NULL ); ++ ++ queue->in_fast_wait = 0; ++ } ++ ++ release_object( queue ); ++} +-- +2.35.3 + +From 23c38272eeeb5c5c81ae734e8d5251a83a8a6dfc Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 16:11:14 -0500 +Subject: [PATCH 21/30] server: Allow creating an event object for client-side + user APC signaling. + +--- + server/protocol.def | 7 +++++++ + server/thread.c | 21 +++++++++++++++++++++ + server/thread.h | 1 + + 3 files changed, 29 insertions(+) + +diff --git a/server/protocol.def b/server/protocol.def +index b9342731964..931d1846a14 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3789,3 +3789,10 @@ enum fast_sync_type + obj_handle_t handle; /* handle to the queue */ + int signaled; /* was the queue signaled? */ + @END ++ ++ ++/* Get an event handle to be used for thread alerts with fast synchronization */ ++@REQ(get_fast_alert_event) ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++@END +diff --git a/server/thread.c b/server/thread.c +index bbcc8fe612a..5c06819821c 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -254,6 +254,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->desc_len = 0; + thread->exit_poll = NULL; + thread->fast_sync = NULL; ++ thread->fast_alert_event = NULL; + + thread->creation_time = current_time; + thread->exit_time = 0; +@@ -470,6 +471,7 @@ static void destroy_thread( struct object *obj ) + close( thread->esync_fd ); + + if (thread->fast_sync) release_object( thread->fast_sync ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1162,6 +1164,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); ++ ++ if (apc->call.type == APC_USER && thread->fast_alert_event) ++ set_event( thread->fast_alert_event ); + } + + return 1; +@@ -1194,6 +1201,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty + apc->executed = 1; + wake_up( &apc->obj, 0 ); + release_object( apc ); ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + return; + } + } +@@ -1208,6 +1217,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + { + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); ++ ++ if (list_empty( &thread->user_apc ) && thread->fast_alert_event) ++ reset_event( thread->fast_alert_event ); + } + + if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) +@@ -2029,3 +2041,12 @@ DECL_HANDLER(get_next_thread) + set_error( STATUS_NO_MORE_ENTRIES ); + release_object( process ); + } ++ ++DECL_HANDLER(get_fast_alert_event) ++{ ++ if (!current->fast_alert_event) ++ current->fast_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( ¤t->user_apc ), NULL ); ++ ++ if (current->fast_alert_event) ++ reply->handle = alloc_handle( current->process, current->fast_alert_event, SYNCHRONIZE, 0 ); ++} +diff --git a/server/thread.h b/server/thread.h +index caabbe5bfd6..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -91,6 +91,7 @@ struct thread + WCHAR *desc; /* thread description string */ + struct timeout_user *exit_poll; /* poll if the thread/process has exited already */ + struct fast_sync *fast_sync; /* fast synchronization object */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +-- +2.35.3 + +From 450c0c88dd2f7d154a8b7c9abc750ad826a7184f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 8 Mar 2021 18:07:23 -0600 +Subject: [PATCH 22/30] ntdll: Introduce a helper to wait on an internal server + handle. + +--- + dlls/ntdll/unix/file.c | 2 +- + dlls/ntdll/unix/process.c | 2 +- + dlls/ntdll/unix/server.c | 17 ++++++++++++++++- + dlls/ntdll/unix/thread.c | 2 +- + dlls/ntdll/unix/unix_private.h | 4 +++- + 5 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index cc8bf0c6e82..0969b480a9c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6016,7 +6016,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void + } + if (handle) + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + NtClose( handle ); + } + else /* Unix lock conflict, sleep a bit and retry */ +diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c +index 078ad75099d..0388dbe1278 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -873,7 +873,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ + + /* wait for the new process info to be ready */ + +- NtWaitForSingleObject( process_info, FALSE, NULL ); ++ server_wait_for_object( process_info, FALSE, NULL ); + SERVER_START_REQ( get_new_process_info ) + { + req->info = wine_server_obj_handle( process_info ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 77e8d5c7566..480927fe179 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -674,6 +674,21 @@ unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT f + } + + ++/* helper function to perform a server-side wait on an internal handle without ++ * using the fast synchronization path */ ++unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ) ++{ ++ select_op_t select_op; ++ UINT flags = SELECT_INTERRUPTIBLE; ++ ++ if (alertable) flags |= SELECT_ALERTABLE; ++ ++ select_op.wait.op = SELECT_WAIT; ++ select_op.wait.handles[0] = wine_server_obj_handle( handle ); ++ return server_wait( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout ); ++} ++ ++ + /*********************************************************************** + * NtContinue (NTDLL.@) + */ +@@ -735,7 +750,7 @@ unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, a + } + else + { +- NtWaitForSingleObject( handle, FALSE, NULL ); ++ server_wait_for_object( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 503230e4634..312d7194da2 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1707,7 +1707,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma + + if (ret == STATUS_PENDING) + { +- NtWaitForSingleObject( context_handle, FALSE, NULL ); ++ server_wait_for_object( context_handle, FALSE, NULL ); + + SERVER_START_REQ( get_thread_context ) + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 795fc148479..adcca4f4f1f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -164,6 +164,8 @@ extern unsigned int server_select( const select_op_t *select_op, data_size_t siz + timeout_t abs_timeout, context_t *context, user_apc_t *user_apc ) DECLSPEC_HIDDEN; + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ) DECLSPEC_HIDDEN; + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -344,7 +346,7 @@ static inline async_data_t server_async( HANDLE handle, struct async_fileio *use + + static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable ) + { +- return NtWaitForSingleObject( handle, alertable, NULL ); ++ return server_wait_for_object( handle, alertable, NULL ); + } + + static inline BOOL in_wow64_call(void) +-- +2.35.3 + +From b9fe98ce5b2ab1815f8640459dd8e3631445d5bf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 9 Mar 2021 12:08:29 -0600 +Subject: [PATCH 23/30] ntdll: Add some traces to synchronization methods. + +--- + dlls/ntdll/unix/sync.c | 59 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 1695e6ed570..e6cbe3d63b4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ HANDLE keyed_event = 0; + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { + if (!timeout) return "(infinite)"; +- return wine_dbgstr_longlong( timeout->QuadPart ); ++ return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC), ++ (long)(timeout->QuadPart % TICKSPERSEC) ); + } + +@@ -258,6 +260,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, initial %d, max %d\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", initial, max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -285,6 +290,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + + if (do_fsync()) +@@ -344,6 +351,8 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, count, previous ); ++ + if (do_fsync()) + return fsync_release_semaphore( handle, count, previous ); + +@@ -368,6 +377,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u, state %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type, state ); ++ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + +@@ -395,6 +407,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -420,6 +434,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_set_event( handle, prev_state ); + +@@ -439,6 +455,8 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_reset_event( handle, prev_state ); + +@@ -468,6 +486,8 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_state %p\n", handle, prev_state ); ++ + if (do_fsync()) + return fsync_pulse_event( handle, prev_state ); + +@@ -525,6 +545,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, owned %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + + if (do_fsync()) +@@ -550,6 +573,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -575,6 +600,8 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, prev_count %p\n", handle, prev_count ); ++ + if (do_fsync()) + return fsync_release_mutex( handle, prev_count ); + +@@ -1260,6 +1287,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, type %u\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", type ); ++ + *handle = 0; + if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -1287,6 +1317,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1340,6 +1372,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + NTSTATUS ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1408,9 +1442,17 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (TRACE_ON(sync)) ++ { ++ TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] ); ++ for (i = 1; i < count; i++) TRACE( ", %p", handles[i] ); ++ TRACE( "}, timeout %s\n", debugstr_timeout(timeout) ); ++ } ++ + if (do_fsync()) + { + NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1428,7 +1470,9 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +- return server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ ret = server_wait( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); ++ TRACE( "-> %#x\n", ret ); ++ return ret; + } + + +@@ -1436,6 +1480,8 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); ++ + if (do_fsync()) + return fsync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1655,6 +1701,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access, + data_size_t len; + struct object_attributes *objattr; + ++ TRACE( "access %#x, name %s, flags %#x\n", access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1679,6 +1728,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + NTSTATUS ret; + ++ TRACE( "access %#x, name %s\n", access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1705,6 +1756,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +@@ -1724,6 +1777,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) ); ++ + if (!handle) handle = keyed_event; + if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1; + if (alertable) flags |= SELECT_ALERTABLE; +-- +2.35.3 + +From d58f3d75cc8714768e413dbb6e870648461d3763 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 6 Apr 2021 15:37:02 -0500 +Subject: [PATCH 24/30] ntdll: Use fast synchronization objects. + +--- + dlls/ntdll/unix/sync.c | 826 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 1 + + 2 files changed, 827 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e6cbe3d63b4..314a0452fda 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -30,9 +30,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #ifdef HAVE_SYS_SYSCALL_H + #include +@@ -45,6 +47,7 @@ + #endif + #include + #include ++#include + #include + #include + #include +@@ -54,6 +57,9 @@ + # include + # include + #endif ++#ifdef HAVE_LINUX_WINESYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -250,6 +256,783 @@ static NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) + } + + ++#ifdef HAVE_LINUX_WINESYNC_H ++ ++/* glibc passes the sigset pointer directly to the linux kernel, but defines ++ * sigset_t to be larger. Manually define the kernel sigset size here. */ ++#define KERNEL_SIGSET_SIZE (64 / 8) /* 64 signals / 8 bits per byte */ ++ ++struct timespec64 ++{ ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static int get_linux_sync_device(void) ++{ ++ static int fast_sync_fd = -2; ++ ++ if (fast_sync_fd == -2) ++ { ++ HANDLE device; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ SERVER_START_REQ( get_linux_sync_device ) ++ { ++ if (!(ret = wine_server_call( req ))) device = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ if (!server_get_unix_fd( device, 0, &fd, &needs_close, NULL, NULL )) ++ { ++ if (InterlockedCompareExchange( &fast_sync_fd, fd, -2 ) != -2) ++ { ++ /* someone beat us to it */ ++ if (needs_close) close( fd ); ++ NtClose( device ); ++ } ++ /* otherwise don't close the device */ ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ NtClose( device ); ++ } ++ } ++ else ++ { ++ InterlockedCompareExchange( &fast_sync_fd, -1, -2 ); ++ } ++ } ++ return fast_sync_fd; ++} ++ ++/* It's possible for synchronization primitives to remain alive even after being ++ * closed, because a thread is still waiting on them. It's rare in practice, and ++ * documented as being undefined behaviour by Microsoft, but it works, and some ++ * applications rely on it. This means we need to refcount handles, and defer ++ * deleting them on the server side until the refcount reaches zero. We do this ++ * by having each client process hold a handle to the fast synchronization ++ * object, as well as a private refcount. When the client refcount reaches zero, ++ * it closes the handle; when all handles are closed, the server deletes the ++ * fast synchronization object. ++ * ++ * We want lookup of objects from the cache to be very fast; ideally, it should ++ * be lock-free. We achieve this by using atomic modifications to "refcount", ++ * and guaranteeing that all other fields are valid and correct *as long as* ++ * refcount is nonzero, and we store the entire structure in memory which will ++ * never be freed. ++ * ++ * This means that acquiring the object can't use a simple atomic increment; it ++ * has to use a compare-and-swap loop to ensure that it doesn't try to increment ++ * an object with a zero refcount. That's still leagues better than a real lock, ++ * though, and release can be a single atomic decrement. ++ * ++ * It also means that threads modifying the cache need to take a lock, to ++ * prevent other threads from writing to it concurrently. ++ * ++ * It's possible for an object currently in use (by a waiter) to be closed and ++ * the same handle immediately reallocated to a different object. This should be ++ * a very rare situation, and in that case we simply don't cache the handle. ++ */ ++struct fast_sync_cache_entry ++{ ++ LONG refcount; ++ unsigned int obj; ++ enum fast_sync_type type; ++ unsigned int access; ++ BOOL closed; ++ /* handle to the underlying fast sync object, stored as obj_handle_t to save ++ * space */ ++ obj_handle_t handle; ++}; ++ ++ ++static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) ++{ ++ /* save the handle now; as soon as the refcount hits 0 we cannot access the ++ * cache anymore */ ++ HANDLE handle = wine_server_ptr_handle( cache->handle ); ++ LONG refcount = InterlockedDecrement( &cache->refcount ); ++ ++ assert( refcount >= 0 ); ++ ++ if (!refcount) ++ { ++ NTSTATUS ret = NtClose( handle ); ++ assert( !ret ); ++ } ++} ++ ++ ++static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) ++{ ++ if (a == b) return TRUE; ++ if (a == FAST_SYNC_AUTO_EVENT && b == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ if (b == FAST_SYNC_AUTO_EVENT && a == FAST_SYNC_MANUAL_EVENT) return TRUE; ++ return FALSE; ++} ++ ++ ++/* returns a pointer to a cache entry; if the object could not be cached, ++ * returns "stack_cache" instead, which should be allocated on stack */ ++static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_type, ACCESS_MASK desired_access, ++ struct fast_sync_cache_entry *stack_cache, ++ struct fast_sync_cache_entry **ret_cache ) ++{ ++ struct fast_sync_cache_entry *cache = stack_cache; ++ NTSTATUS ret; ++ ++ *ret_cache = stack_cache; ++ ++ SERVER_START_REQ( get_linux_sync_obj ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ cache->handle = reply->handle; ++ cache->access = reply->access; ++ cache->type = reply->type; ++ cache->obj = reply->obj; ++ cache->refcount = 1; ++ cache->closed = FALSE; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if (!ret && (cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int device, unsigned int obj, ULONG count, ULONG *prev_count ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ args.count = count; ++ ret = ioctl( device, WINESYNC_IOC_PUT_SEM, &args ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( device, cache->obj, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int device, unsigned int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct winesync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ args.sem = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_SEM, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->CurrentCount = args.count; ++ info->MaximumCount = args.max; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_SET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_RESET_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int device, unsigned int obj, LONG *prev_state ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_PULSE_EVENT, &args ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( device, cache->obj, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int device, unsigned int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ args.event = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_EVENT, &args ); ++ ++ if (ret < 0) ++ return errno_to_status( errno ); ++ info->EventType = (type == FAST_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent; ++ info->EventState = args.signaled; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( device, cache->obj, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int device, unsigned int obj, LONG *prev_count ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( device, WINESYNC_IOC_PUT_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_MUTANT_LIMIT_EXCEEDED; ++ else if (errno == EPERM) ++ return STATUS_MUTANT_NOT_OWNED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( device, cache->obj, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int device, unsigned int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct winesync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.mutex = obj; ++ ret = ioctl( device, WINESYNC_IOC_READ_MUTEX, &args ); ++ ++ if (ret < 0) ++ { ++ if (errno == EOWNERDEAD) ++ { ++ info->AbandonedState = TRUE; ++ info->OwnedByCaller = FALSE; ++ info->CurrentCount = 1; ++ return STATUS_SUCCESS; ++ } ++ else ++ return errno_to_status( errno ); ++ } ++ info->AbandonedState = FALSE; ++ info->OwnedByCaller = (args.owner == GetCurrentThreadId()); ++ info->CurrentCount = 1 - args.count; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( device, cache->obj, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static void timespec64_from_timeout( struct timespec64 *timespec, const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ clock_gettime( CLOCK_MONOTONIC, &now ); ++ ++ if (timeout->QuadPart <= 0) ++ { ++ relative = -timeout->QuadPart; ++ } ++ else ++ { ++ LARGE_INTEGER system_now; ++ ++ /* the system clock is REALTIME, so we need to convert to ++ * relative time first */ ++ NtQuerySystemTime( &system_now ); ++ relative = timeout->QuadPart - system_now.QuadPart; ++ } ++ ++ timespec->tv_sec = now.tv_sec + (relative / TICKSPERSEC); ++ timespec->tv_nsec = now.tv_nsec + ((relative % TICKSPERSEC) * 100); ++ if (timespec->tv_nsec >= 1000000000) ++ { ++ timespec->tv_nsec -= 1000000000; ++ ++timespec->tv_sec; ++ } ++} ++ ++static void select_queue( HANDLE queue ) ++{ ++ SERVER_START_REQ( fast_select_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static void unselect_queue( HANDLE queue, BOOL signaled ) ++{ ++ SERVER_START_REQ( fast_unselect_queue ) ++ { ++ req->handle = wine_server_obj_handle( queue ); ++ req->signaled = signaled; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++static unsigned int get_fast_alert_obj(void) ++{ ++ struct ntdll_thread_data *data = ntdll_get_thread_data(); ++ struct fast_sync_cache_entry stack_cache, *cache; ++ HANDLE alert_handle; ++ NTSTATUS ret; ++ ++ if (!data->fast_alert_obj) ++ { ++ SERVER_START_REQ( get_fast_alert_event ) ++ { ++ if ((ret = wine_server_call( req ))) ++ ERR( "failed to get fast alert event, status %#x\n", ret ); ++ alert_handle = wine_server_ptr_handle( reply->handle ); ++ } ++ SERVER_END_REQ; ++ ++ if ((ret = get_fast_sync_obj( alert_handle, 0, SYNCHRONIZE, &stack_cache, &cache ))) ++ ERR( "failed to get fast alert obj, status %#x\n", ret ); ++ data->fast_alert_obj = cache->obj; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const unsigned int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct winesync_wait_args args = {0}; ++ struct timespec64 timespec; ++ uintptr_t timeout_ptr = 0; ++ unsigned long request; ++ int ret; ++ ++ if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) ++ { ++ timeout_ptr = (uintptr_t)×pec; ++ timespec64_from_timeout( ×pec, timeout ); ++ } ++ args.objs = (uintptr_t)objs; ++ args.count = count; ++ args.owner = GetCurrentThreadId(); ++ args.index = ~0u; ++ ++ if (alertable) ++ args.alert = get_fast_alert_obj(); ++ ++ if (wait_any || count == 1) ++ request = WINESYNC_IOC_WAIT_ANY; ++ else ++ request = WINESYNC_IOC_WAIT_ALL; ++ ++ args.timeout = timeout_ptr; ++ do ++ { ++ ret = ioctl( device, request, &args ); ++ } while (ret < 0 && errno == EINTR); ++ ++ if (!ret) ++ { ++ if (args.index == count) ++ { ++ static const LARGE_INTEGER timeout; ++ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); ++ assert( ret == STATUS_USER_APC ); ++ return ret; ++ } ++ ++ return wait_any ? args.index : 0; ++ } ++ else if (errno == EOWNERDEAD) ++ return STATUS_ABANDONED + (wait_any ? args.index : 0); ++ else if (errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return errno_to_status( errno ); ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry stack_cache[64], *cache[64]; ++ unsigned int objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ for (i = 0; i < count; ++i) ++ { ++ if ((ret = get_fast_sync_obj( handles[i], 0, SYNCHRONIZE, &stack_cache[i], &cache[i] ))) ++ { ++ for (j = 0; j < i; ++j) ++ release_fast_sync_obj( cache[j] ); ++ return ret; ++ } ++ if (cache[i]->type == FAST_SYNC_QUEUE) ++ queue = handles[i]; ++ ++ objs[i] = cache[i]->obj; ++ } ++ ++ if (queue) select_queue( queue ); ++ ++ ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); ++ ++ if (queue) unselect_queue( queue, handles[ret] == queue ); ++ ++ for (i = 0; i < count; ++i) ++ release_fast_sync_obj( cache[i] ); ++ ++ return ret; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct fast_sync_cache_entry signal_stack_cache, *signal_cache; ++ struct fast_sync_cache_entry wait_stack_cache, *wait_cache; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ int device; ++ ++ if ((device = get_linux_sync_device()) < 0) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if ((ret = get_fast_sync_obj( signal, 0, 0, &signal_stack_cache, &signal_cache ))) ++ return ret; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ if (!(signal_cache->access & SEMAPHORE_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ if (!(signal_cache->access & EVENT_MODIFY_STATE)) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ break; ++ ++ default: ++ /* can't be signaled */ ++ release_fast_sync_obj( signal_cache ); ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ ++ if ((ret = get_fast_sync_obj( wait, 0, SYNCHRONIZE, &wait_stack_cache, &wait_cache ))) ++ { ++ release_fast_sync_obj( signal_cache ); ++ return ret; ++ } ++ ++ if (wait_cache->type == FAST_SYNC_QUEUE) ++ queue = wait; ++ ++ switch (signal_cache->type) ++ { ++ case FAST_SYNC_SEMAPHORE: ++ ret = linux_release_semaphore_obj( device, signal_cache->obj, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( device, signal_cache->obj, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->obj, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_release_mutex( HANDLE handle, LONG *prev_count ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ return STATUS_NOT_IMPLEMENTED; ++} ++ ++#endif ++ ++ + /****************************************************************************** + * NtCreateSemaphore (NTDLL.@) + */ +@@ -329,6 +1112,12 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + ++ if ((ret = fast_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -353,6 +1142,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + ++ if ((ret = fast_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -436,6 +1228,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_set_event( handle ); + ++ if ((ret = fast_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -457,6 +1252,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_reset_event( handle ); + ++ if ((ret = fast_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -488,6 +1286,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + if (do_esync()) + return esync_pulse_event( handle ); + ++ if ((ret = fast_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -520,6 +1321,12 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + ++ if ((ret = fast_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -602,6 +1409,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + ++ if ((ret = fast_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -632,6 +1442,12 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + ++ if ((ret = fast_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED) ++ { ++ if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION); ++ return ret; ++ } ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1453,6 +2269,12 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + return ret; + } + ++ if ((ret = fast_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ { ++ TRACE( "-> %#x\n", ret ); ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1479,6 +2301,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + { + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; ++ NTSTATUS ret; + + TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) ); + +@@ -1490,6 +2313,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + + if (!signal) return STATUS_INVALID_HANDLE; + ++ if ((ret = fast_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index adcca4f4f1f..9158a4736ad 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -61,6 +61,7 @@ struct ntdll_thread_data + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ + void *jmp_buf; /* setjmp buffer for exception handling */ ++ unsigned int fast_alert_obj; /* linux object for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.35.3 + +From 7b6ede8222e908ca8c63447e14981d50fe24ac77 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Tue, 20 Apr 2021 17:55:59 -0500 +Subject: [PATCH 25/30] ntdll: Use server_wait_for_object() when waiting on + only the queue object. + +--- + dlls/ntdll/unix/sync.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 314a0452fda..681148bce49 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -880,6 +880,17 @@ static NTSTATUS fast_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any, + objs[i] = cache[i]->obj; + } + ++ /* It's common to wait on the message queue alone. Some applications wait ++ * on it in fast paths, with a zero timeout. Since we take two server calls ++ * instead of one when going through fast_wait_objs(), and since we only ++ * need to go through that path if we're waiting on other objects, just ++ * delegate to the server if we're only waiting on the message queue. */ ++ if (count == 1 && queue) ++ { ++ release_fast_sync_obj( cache[0] ); ++ return server_wait_for_object( handles[0], alertable, timeout ); ++ } ++ + if (queue) select_queue( queue ); + + ret = linux_wait_objs( device, count, objs, wait_any, alertable, timeout ); +-- +2.35.3 + +From ba7f120813b76f73b8743c3185d1aaf6928d70d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 12 Mar 2021 15:04:17 -0600 +Subject: [PATCH 26/30] ntdll: Cache fast synchronization objects. + +--- + dlls/ntdll/unix/server.c | 11 +- + dlls/ntdll/unix/sync.c | 200 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/unix_private.h | 4 + + 3 files changed, 202 insertions(+), 13 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 480927fe179..dda584be554 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1661,12 +1661,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE + return result.dup_handle.status; + } + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just + * retrieve it again */ + if (options & DUPLICATE_CLOSE_SOURCE) ++ { + fd = remove_fd_from_cache( source ); ++ close_fast_sync_obj( source ); ++ } + + SERVER_START_REQ( dup_handle ) + { +@@ -1722,6 +1727,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0) + return STATUS_SUCCESS; + ++ /* hold fd_cache_mutex to prevent the fd from being added again between the ++ * call to remove_fd_from_cache and close_handle */ + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + /* always remove the cached fd; if the server request fails we'll just +@@ -1734,6 +1741,8 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + if (do_esync()) + esync_close( handle ); + ++ close_fast_sync_obj( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 681148bce49..8810ea5176c 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -320,6 +320,12 @@ static int get_linux_sync_device(void) + * it closes the handle; when all handles are closed, the server deletes the + * fast synchronization object. + * ++ * We also need this for signal-and-wait. The signal and wait operations aren't ++ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE ++ * for the wait—we need to either do both operations or neither. That means we ++ * need to grab references to both objects, and prevent them from being ++ * destroyed before we're done with them. ++ * + * We want lookup of objects from the cache to be very fast; ideally, it should + * be lock-free. We achieve this by using atomic modifications to "refcount", + * and guaranteeing that all other fields are valid and correct *as long as* +@@ -362,12 +368,139 @@ static void release_fast_sync_obj( struct fast_sync_cache_entry *cache ) + + if (!refcount) + { +- NTSTATUS ret = NtClose( handle ); ++ NTSTATUS ret; ++ ++ /* we can't call NtClose here as we may be inside fd_cache_mutex */ ++ SERVER_START_REQ( close_handle ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ ret = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ + assert( !ret ); + } + } + + ++#define FAST_SYNC_CACHE_BLOCK_SIZE (65536 / sizeof(struct fast_sync_cache_entry)) ++#define FAST_SYNC_CACHE_ENTRIES 128 ++ ++static struct fast_sync_cache_entry *fast_sync_cache[FAST_SYNC_CACHE_ENTRIES]; ++static struct fast_sync_cache_entry fast_sync_cache_initial_block[FAST_SYNC_CACHE_BLOCK_SIZE]; ++ ++static inline unsigned int fast_sync_handle_to_index( HANDLE handle, unsigned int *entry ) ++{ ++ unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; ++ *entry = idx / FAST_SYNC_CACHE_BLOCK_SIZE; ++ return idx % FAST_SYNC_CACHE_BLOCK_SIZE; ++} ++ ++ ++static struct fast_sync_cache_entry *cache_fast_sync_obj( HANDLE handle, obj_handle_t fast_sync, int obj, ++ enum fast_sync_type type, unsigned int access ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ sigset_t sigset; ++ int refcount; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return NULL; ++ } ++ ++ if (!fast_sync_cache[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fast_sync_cache[0] = fast_sync_cache_initial_block; ++ else ++ { ++ static const size_t size = FAST_SYNC_CACHE_BLOCK_SIZE * sizeof(struct fast_sync_cache_entry); ++ void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return NULL; ++ if (InterlockedCompareExchangePointer( (void **)&fast_sync_cache[entry], ptr, NULL )) ++ munmap( ptr, size ); /* someone beat us to it */ ++ } ++ } ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same ++ * race between this function and NtClose. That is, prevent the object from ++ * being cached again between close_fast_sync_obj() and close_handle. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (InterlockedCompareExchange( &cache->refcount, 0, 0 )) ++ { ++ /* We lost the race with another thread trying to cache this object, or ++ * the handle is currently being used for another object (i.e. it was ++ * closed and then reused). We have no way of knowing which, and in the ++ * latter case we can't cache this object until the old one is ++ * completely destroyed, so always return failure. */ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ return NULL; ++ } ++ ++ cache->handle = fast_sync; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ /* Make sure we set the other members before the refcount; this store needs ++ * release semantics [paired with the load in get_cached_fast_sync_obj()]. ++ * Set the refcount to 2 (one for the handle, one for the caller). */ ++ refcount = InterlockedExchange( &cache->refcount, 2 ); ++ assert( !refcount ); ++ ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ return cache; ++} ++ ++ ++/* returns the previous value */ ++static inline LONG interlocked_inc_if_nonzero( LONG *dest ) ++{ ++ LONG val, tmp; ++ for (val = *dest;; val = tmp) ++ { ++ if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val) ++ break; ++ } ++ return val; ++} ++ ++ ++static struct fast_sync_cache_entry *get_cached_fast_sync_obj( HANDLE handle ) ++{ ++ unsigned int entry, idx = fast_sync_handle_to_index( handle, &entry ); ++ struct fast_sync_cache_entry *cache; ++ ++ if (entry >= FAST_SYNC_CACHE_ENTRIES || !fast_sync_cache[entry]) ++ return NULL; ++ ++ cache = &fast_sync_cache[entry][idx]; ++ ++ /* this load needs acquire semantics [paired with the store in ++ * cache_fast_sync_obj()] */ ++ if (!interlocked_inc_if_nonzero( &cache->refcount )) ++ return NULL; ++ ++ if (cache->closed) ++ { ++ /* The object is still being used, but "handle" has been closed. The ++ * handle value might have been reused for another object in the ++ * meantime, in which case we have to report that valid object, so ++ * force the caller to check the server. */ ++ release_fast_sync_obj( cache ); ++ return NULL; ++ } ++ ++ return cache; ++} ++ ++ + static BOOL fast_sync_types_match( enum fast_sync_type a, enum fast_sync_type b ) + { + if (a == b) return TRUE; +@@ -383,39 +516,78 @@ static NTSTATUS get_fast_sync_obj( HANDLE handle, enum fast_sync_type desired_ty + struct fast_sync_cache_entry *stack_cache, + struct fast_sync_cache_entry **ret_cache ) + { +- struct fast_sync_cache_entry *cache = stack_cache; ++ struct fast_sync_cache_entry *cache; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; + NTSTATUS ret; ++ int obj; + +- *ret_cache = stack_cache; ++ /* try to find it in the cache already */ ++ if ((cache = get_cached_fast_sync_obj( handle ))) ++ { ++ *ret_cache = cache; ++ return STATUS_SUCCESS; ++ } + ++ /* try to retrieve it from the server */ + SERVER_START_REQ( get_linux_sync_obj ) + { + req->handle = wine_server_obj_handle( handle ); + if (!(ret = wine_server_call( req ))) + { +- cache->handle = reply->handle; +- cache->access = reply->access; +- cache->type = reply->type; +- cache->obj = reply->obj; +- cache->refcount = 1; +- cache->closed = FALSE; ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ obj = reply->obj; + } + } + SERVER_END_REQ; + +- if (!ret && desired_type && !fast_sync_types_match( cache->type, desired_type )) ++ if (ret) return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, obj, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->obj = obj; ++ cache->type = type; ++ cache->access = access; ++ cache->closed = FALSE; ++ cache->refcount = 1; ++ } ++ ++ *ret_cache = cache; ++ ++ if (desired_type && !fast_sync_types_match( cache->type, desired_type )) + { + release_fast_sync_obj( cache ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + +- if (!ret && (cache->access & desired_access) != desired_access) ++ if ((cache->access & desired_access) != desired_access) + { + release_fast_sync_obj( cache ); + return STATUS_ACCESS_DENIED; + } + +- return ret; ++ return STATUS_SUCCESS; ++} ++ ++ ++/* caller must hold fd_cache_mutex */ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++ struct fast_sync_cache_entry *cache = get_cached_fast_sync_obj( handle ); ++ ++ if (cache) ++ { ++ cache->closed = TRUE; ++ /* once for the reference we just grabbed, and once for the handle */ ++ release_fast_sync_obj( cache ); ++ release_fast_sync_obj( cache ); ++ } + } + + +@@ -989,6 +1161,10 @@ static NTSTATUS fast_signal_and_wait( HANDLE signal, HANDLE wait, + + #else + ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ + static NTSTATUS fast_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count ) + { + return STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9158a4736ad..42943d6a84a 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -158,6 +158,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; + extern void start_server( BOOL debug ) DECLSPEC_HIDDEN; + ++extern pthread_mutex_t fd_cache_mutex DECLSPEC_HIDDEN; ++ + extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; + extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; + extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset ) DECLSPEC_HIDDEN; +@@ -281,6 +283,8 @@ extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, U + + extern void dbg_init(void) DECLSPEC_HIDDEN; + ++extern void close_fast_sync_obj( HANDLE handle ) DECLSPEC_HIDDEN; ++ + extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; +-- +2.35.3 + +From aab4ffd48a4334f92b5dd945cd0b3a6b365a654f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sat, 13 Mar 2021 16:20:30 -0600 +Subject: [PATCH 28/30] server: Allow disabling fast synchronization support. + +--- + server/fast_sync.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 6b738519c7a..8e7ac54d16c 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -126,6 +126,12 @@ static struct linux_device *get_linux_device(void) + struct linux_device *device; + int unix_fd; + ++ if (getenv( "WINE_DISABLE_FAST_SYNC" ) && atoi( getenv( "WINE_DISABLE_FAST_SYNC" ) )) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++ } ++ + if (linux_device_object) + return (struct linux_device *)grab_object( linux_device_object ); + +-- +2.35.3 + +From cd182b81970779fdafc8465e86240d5dbe1be18f Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 14 Mar 2021 11:08:02 -0500 +Subject: [PATCH 29/30] server: Add a message to signal that fast + synchronization is indeed active. + +--- + server/fast_sync.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/server/fast_sync.c b/server/fast_sync.c +index 8e7ac54d16c..d7a2234ef21 100644 +--- a/server/fast_sync.c ++++ b/server/fast_sync.c +@@ -407,6 +407,10 @@ DECL_HANDLER(get_linux_sync_obj) + { + #ifdef HAVE_LINUX_WINESYNC_H + struct object *obj; ++ static int once; ++ ++ if (!once++) ++ fprintf( stderr, "wine: using fast synchronization.\n" ); + + if ((obj = get_handle_obj( current->process, req->handle, 0, NULL ))) + { +-- +2.35.3 + +From f5221be03bc4af17cbfc8448bd34a32868c979be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 21 Apr 2022 17:21:17 -0500 +Subject: [PATCH 30/30] make_req + +--- + include/wine/server_protocol.h | 99 +++++++++++++++++++++++++++++++++- + server/request.h | 28 ++++++++++ + server/trace.c | 57 ++++++++++++++++++++ + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 8f883b2d97e..76b2a91f295 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5443,6 +5443,88 @@ struct get_esync_apc_fd_reply + }; + + ++enum fast_sync_type ++{ ++ FAST_SYNC_SEMAPHORE = 1, ++ FAST_SYNC_MUTEX, ++ FAST_SYNC_AUTO_EVENT, ++ FAST_SYNC_MANUAL_EVENT, ++ FAST_SYNC_AUTO_SERVER, ++ FAST_SYNC_MANUAL_SERVER, ++ FAST_SYNC_QUEUE, ++}; ++ ++ ++ ++struct get_linux_sync_device_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_linux_sync_device_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ ++ ++struct get_linux_sync_obj_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct get_linux_sync_obj_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ int obj; ++ int type; ++ unsigned int access; ++}; ++ ++ ++ ++struct fast_select_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++}; ++struct fast_select_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct fast_unselect_queue_request ++{ ++ struct request_header __header; ++ obj_handle_t handle; ++ int signaled; ++ char __pad_20[4]; ++}; ++struct fast_unselect_queue_reply ++{ ++ struct reply_header __header; ++}; ++ ++ ++ ++struct get_fast_alert_event_request ++{ ++ struct request_header __header; ++ char __pad_12[4]; ++}; ++struct get_fast_alert_event_reply ++{ ++ struct reply_header __header; ++ obj_handle_t handle; ++ char __pad_12[4]; ++}; ++ ++ + enum request + { + REQ_new_process, +@@ -5720,6 +5802,11 @@ enum request + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, ++ REQ_get_linux_sync_device, ++ REQ_get_linux_sync_obj, ++ REQ_fast_select_queue, ++ REQ_fast_unselect_queue, ++ REQ_get_fast_alert_event, + REQ_NB_REQUESTS + }; + +@@ -6002,6 +6089,11 @@ union generic_request + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; ++ struct get_linux_sync_device_request get_linux_sync_device_request; ++ struct get_linux_sync_obj_request get_linux_sync_obj_request; ++ struct fast_select_queue_request fast_select_queue_request; ++ struct fast_unselect_queue_request fast_unselect_queue_request; ++ struct get_fast_alert_event_request get_fast_alert_event_request; + }; + union generic_reply + { +@@ -6282,6 +6374,11 @@ union generic_reply + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; ++ struct get_linux_sync_device_reply get_linux_sync_device_reply; ++ struct get_linux_sync_obj_reply get_linux_sync_obj_reply; ++ struct fast_select_queue_reply fast_select_queue_reply; ++ struct fast_unselect_queue_reply fast_unselect_queue_reply; ++ struct get_fast_alert_event_reply get_fast_alert_event_reply; + }; + + /* ### protocol_version begin ### */ +diff --git a/server/request.h b/server/request.h +index 9ed2f898e6d..a4b33f6ac6f 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -394,6 +394,11 @@ DECL_HANDLER(terminate_job); + DECL_HANDLER(get_esync_fd); + DECL_HANDLER(esync_msgwait); + DECL_HANDLER(get_esync_apc_fd); ++DECL_HANDLER(get_linux_sync_device); ++DECL_HANDLER(get_linux_sync_obj); ++DECL_HANDLER(fast_select_queue); ++DECL_HANDLER(fast_unselect_queue); ++DECL_HANDLER(get_fast_alert_event); + + #ifdef WANT_REQUEST_HANDLERS + +@@ -675,6 +680,11 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, ++ (req_handler)req_get_linux_sync_device, ++ (req_handler)req_get_linux_sync_obj, ++ (req_handler)req_fast_select_queue, ++ (req_handler)req_fast_unselect_queue, ++ (req_handler)req_get_fast_alert_event, + }; + + C_ASSERT( sizeof(abstime_t) == 8 ); +@@ -2254,6 +2264,24 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 ); + C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); + C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); + C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_device_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_request, handle) == 12 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, handle) == 8 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, obj) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, type) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 20 ); ++C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 ); ++C_ASSERT( FIELD_OFFSET(struct fast_select_queue_request, handle) == 12 ); ++C_ASSERT( sizeof(struct fast_select_queue_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, handle) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct fast_unselect_queue_request, signaled) == 16 ); ++C_ASSERT( sizeof(struct fast_unselect_queue_request) == 24 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_request) == 16 ); ++C_ASSERT( FIELD_OFFSET(struct get_fast_alert_event_reply, handle) == 8 ); ++C_ASSERT( sizeof(struct get_fast_alert_event_reply) == 16 ); + + #endif /* WANT_REQUEST_HANDLERS */ + +diff --git a/server/trace.c b/server/trace.c +index d71561e1247..59afc88cc6c 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4485,6 +4485,48 @@ static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request + { + } + ++static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req ) ++{ ++} ++ ++static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", obj=%d", req->obj ); ++ fprintf( stderr, ", type=%d", req->type ); ++ fprintf( stderr, ", access=%08x", req->access ); ++} ++ ++static void dump_fast_select_queue_request( const struct fast_select_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ ++static void dump_fast_unselect_queue_request( const struct fast_unselect_queue_request *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++ fprintf( stderr, ", signaled=%d", req->signaled ); ++} ++ ++static void dump_get_fast_alert_event_request( const struct get_fast_alert_event_request *req ) ++{ ++} ++ ++static void dump_get_fast_alert_event_reply( const struct get_fast_alert_event_reply *req ) ++{ ++ fprintf( stderr, " handle=%04x", req->handle ); ++} ++ + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_new_process_request, + (dump_func)dump_get_new_process_info_request, +@@ -4761,6 +4803,11 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, ++ (dump_func)dump_get_linux_sync_device_request, ++ (dump_func)dump_get_linux_sync_obj_request, ++ (dump_func)dump_fast_select_queue_request, ++ (dump_func)dump_fast_unselect_queue_request, ++ (dump_func)dump_get_fast_alert_event_request, + }; + + static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { +@@ -5039,6 +5086,11 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, ++ (dump_func)dump_get_linux_sync_device_reply, ++ (dump_func)dump_get_linux_sync_obj_reply, ++ NULL, ++ NULL, ++ (dump_func)dump_get_fast_alert_event_reply, + }; + + static const char * const req_names[REQ_NB_REQUESTS] = { +@@ -5317,6 +5369,11 @@ static const char * const req_names[REQ_NB_REQUESTS] = { + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", ++ "get_linux_sync_device", ++ "get_linux_sync_obj", ++ "fast_select_queue", ++ "fast_unselect_queue", ++ "get_fast_alert_event", + }; + + static const struct +-- +2.35.3 + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..a1ed9961c00 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -60,7 +61,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 86bb7dce85a..0e09e4e42d9 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -167,7 +168,7 @@ int do_fsync(void) + } + + syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/esync.c b/server/esync.c +index f180a545314..9a91ce82cd3 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -50,7 +51,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync() && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + + return do_esync_cached; + #else +diff --git a/server/fsync.c b/server/fsync.c +index 7e4ff469300..11ff602cac3 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -58,7 +59,7 @@ int do_fsync(void) + if (do_fsync_cached == -1) + { + syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); +- do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS && getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC")); + } + + return do_fsync_cached; +diff --git a/server/main.c b/server/main.c +index 6c4d8117514..701b99cbf77 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -231,14 +232,17 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + +- if (do_fsync()) ++ if (getenv("WINE_DISABLE_FAST_SYNC") && atoi(getenv("WINE_DISABLE_FAST_SYNC"))) ++ { ++ if (do_fsync()) + fsync_init(); + +- if (do_esync()) ++ if (do_esync()) + esync_init(); + +- if (!do_fsync() && !do_esync()) ++ if (!do_fsync() && !do_esync()) + fprintf( stderr, "wineserver: using server-side synchronization.\n" ); ++ } + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); diff --git a/wine-tkg-git/wine-tkg-patches/misc/steam/steam b/wine-tkg-git/wine-tkg-patches/misc/steam/steam index e78dc8997..e8b703a9f 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/steam/steam +++ b/wine-tkg-git/wine-tkg-patches/misc/steam/steam @@ -2,7 +2,7 @@ # steam crossover hack for store/web functionality # https://bugs.winehq.org/show_bug.cgi?id=39403 - if [ "$_steam_fix" = "true" ]; then + if [ "$_steam_fix" = "true" ] && ! git merge-base --is-ancestor 917a206b01c82170a862e8497cbe26b6f1bfade0 HEAD; then if git merge-base --is-ancestor 712ae337fe02c2e222e7c3067e5f624160bb84a1 HEAD; then _patchname='steam.patch' && _patchmsg="Applied steam crossover hack" && nonuser_patcher else diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-59485f0.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-59485f0.patch new file mode 100644 index 000000000..effa0b56e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-59485f0.patch @@ -0,0 +1,586 @@ +From e6c96f88d8f78a9890a5b96a8e85406ad9695f3c Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 2 Nov 2020 23:03:20 +0300 +Subject: [PATCH] ntdll: (HACK) Add variable to report all logical CPUs as + physical cores. + +--- + dlls/ntdll/unix/system.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c +index 0b83761ed46..bf3d6c34f14 100644 +--- a/dlls/ntdll/unix/system.c ++++ b/dlls/ntdll/unix/system.c +@@ -830,7 +830,8 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + static const char core_info[] = "/sys/devices/system/cpu/cpu%u/topology/%s"; + static const char cache_info[] = "/sys/devices/system/cpu/cpu%u/cache/index%u/%s"; + static const char numa_info[] = "/sys/devices/system/node/node%u/cpumap"; +- ++ const char *env_fake_logical_cores = getenv("WINE_LOGICAL_CPUS_AS_CORES"); ++ BOOL fake_logical_cpus_as_cores = env_fake_logical_cores && atoi(env_fake_logical_cores); + FILE *fcpu_list, *fnuma_list, *f; + DWORD len = 0, beg, end, i, j, r, num_cpus = 0, max_cpus = 0; + char op, name[MAX_PATH]; +@@ -902,7 +903,7 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + */ + /* Mask of logical threads sharing same physical core in kernel core numbering. */ + snprintf(name, sizeof(name), core_info, i, "thread_siblings"); +- if(!sysfs_parse_bitmap(name, &thread_mask)) thread_mask = 1< +Date: Fri, 20 Nov 2020 18:01:11 +0300 +Subject: [PATCH] ntdll: Implement CPU topology override. + +--- + dlls/ntdll/unix/server.c | 3 + + dlls/ntdll/unix/system.c | 167 ++++++++++++++++++++++++++++++--- + dlls/ntdll/unix/thread.c | 15 ++- + dlls/ntdll/unix/unix_private.h | 1 + + include/wine/server_protocol.h | 9 +- + server/process.c | 7 ++ + server/process.h | 1 + + server/protocol.def | 7 ++ + server/thread.c | 20 +++- + server/trace.c | 19 ++++ + 10 files changed, 228 insertions(+), 21 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index b1d3e863240..7b0c7c32f58 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1467,6 +1467,7 @@ void CDECL server_init_process_done( void *relay ) + void server_init_process_done(void) + { + void *entry, *teb; ++ struct cpu_topology_override *cpu_override = get_cpu_topology_override(); + unsigned int status; + int suspend; + +@@ -1488,6 +1489,8 @@ void CDECL server_init_process_done( void *relay ) + /* Signal the parent process to continue */ + SERVER_START_REQ( init_process_done ) + { ++ if (cpu_override) ++ wine_server_add_data( req, cpu_override, sizeof(*cpu_override) ); + req->teb = wine_server_client_ptr( teb ); + req->peb = NtCurrentTeb64() ? NtCurrentTeb64()->Peb : wine_server_client_ptr( peb ); + #ifdef __i386__ +diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c +index bf3d6c34f14..fc8165f2828 100644 +--- a/dlls/ntdll/unix/system.c ++++ b/dlls/ntdll/unix/system.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #ifdef HAVE_SYS_PARAM_H +@@ -165,6 +166,12 @@ struct smbios_boot_info + static unsigned int logical_proc_info_len, logical_proc_info_alloc_len; + static SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *logical_proc_info_ex; + static unsigned int logical_proc_info_ex_size, logical_proc_info_ex_alloc_size; ++static struct ++{ ++ struct cpu_topology_override mapping; ++ BOOL smt; ++} ++cpu_override; + + /******************************************************************************* + * Architecture specific feature detection for CPUs +@@ -475,6 +482,88 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) + + #endif /* End architecture specific feature detection for CPUs */ + ++static void fill_cpu_override(unsigned int host_cpu_count) ++{ ++ const char *env_override = getenv("WINE_CPU_TOPOLOGY"); ++ unsigned int i; ++ char *s; ++ ++ if (!env_override) ++ return; ++ ++ cpu_override.mapping.cpu_count = strtol(env_override, &s, 10); ++ if (s == env_override) ++ goto error; ++ ++ if (!cpu_override.mapping.cpu_count || cpu_override.mapping.cpu_count > MAXIMUM_PROCESSORS) ++ { ++ ERR("Invalid logical CPU count %u, limit %u.\n", cpu_override.mapping.cpu_count, MAXIMUM_PROCESSORS); ++ goto error; ++ } ++ ++ if (tolower(*s) == 's') ++ { ++ cpu_override.mapping.cpu_count *= 2; ++ if (cpu_override.mapping.cpu_count > MAXIMUM_PROCESSORS) ++ { ++ ERR("Logical CPU count exceeds limit %u.\n", MAXIMUM_PROCESSORS); ++ goto error; ++ } ++ cpu_override.smt = TRUE; ++ ++s; ++ } ++ if (*s != ':') ++ goto error; ++ ++s; ++ for (i = 0; i < cpu_override.mapping.cpu_count; ++i) ++ { ++ char *next; ++ ++ if (i) ++ { ++ if (*s != ',') ++ { ++ if (!*s) ++ ERR("Incomplete host CPU mapping string, %u CPUs mapping required.\n", ++ cpu_override.mapping.cpu_count); ++ goto error; ++ } ++ ++s; ++ } ++ ++ cpu_override.mapping.host_cpu_id[i] = strtol(s, &next, 10); ++ if (next == s) ++ goto error; ++ if (cpu_override.mapping.host_cpu_id[i] >= host_cpu_count) ++ { ++ ERR("Invalid host CPU index %u (host_cpu_count %u).\n", ++ cpu_override.mapping.host_cpu_id[i], host_cpu_count); ++ goto error; ++ } ++ s = next; ++ } ++ if (*s) ++ goto error; ++ ++ ERR("Overriding CPU configuration, %u logical CPUs, host CPUs ", cpu_override.mapping.cpu_count); ++ for (i = 0; i < cpu_override.mapping.cpu_count; ++i) ++ { ++ if (i) ++ ERR(","); ++ ERR("%u", cpu_override.mapping.host_cpu_id[i]); ++ } ++ ERR("\n"); ++ return; ++error: ++ cpu_override.mapping.cpu_count = 0; ++ ERR("Invalid WINE_CPU_TOPOLOGY string %s (%s).\n", debugstr_a(env_override), debugstr_a(s)); ++} ++ ++struct cpu_topology_override *get_cpu_topology_override(void) ++{ ++ return cpu_override.mapping.cpu_count ? &cpu_override.mapping : NULL; ++} ++ + static BOOL grow_logical_proc_buf(void) + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *new_data; +@@ -942,6 +942,12 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + if (op == '-') fscanf(fcpu_list, "%u%c ", &end, &op); + else end = beg; + ++ if (cpu_override.mapping.cpu_count) ++ { ++ beg = 0; ++ end = cpu_override.mapping.cpu_count - 1; ++ } ++ + for(i = beg; i <= end; i++) + { + DWORD phys_core = 0; +@@ -956,7 +956,9 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + continue; + } + +- snprintf(name, sizeof(name), core_info, i, "physical_package_id"); ++ snprintf(name, sizeof(name), core_info, cpu_override.mapping.cpu_count ? cpu_override.mapping.host_cpu_id[i] : i, ++ "physical_package_id"); ++ + f = fopen(name, "r"); + if (f) + { +@@ -981,19 +981,34 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + + /* Mask of logical threads sharing same physical core in kernel core numbering. */ + snprintf(name, sizeof(name), core_info, i, "thread_siblings"); +- if(fake_logical_cpus_as_cores || !sysfs_parse_bitmap(name, &thread_mask)) thread_mask = (ULONG_PTR)1<NumberOfProcessors = num; ++ ++ fill_cpu_override(num); ++ ++ peb->NumberOfProcessors = cpu_override.mapping.cpu_count ++ ? cpu_override.mapping.cpu_count : num; + get_cpuinfo( &cpu_info ); + TRACE( "<- CPU arch %d, level %d, rev %d, features 0x%x\n", + cpu_info.Architecture, cpu_info.Level, cpu_info.Revision, cpu_info.FeatureSet ); +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index ca5dac43bb0..bc6a5451611 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1323,7 +1323,20 @@ ULONG WINAPI NtGetCurrentProcessorNumber(void) + + #if defined(__linux__) && defined(__NR_getcpu) + int res = syscall(__NR_getcpu, &processor, NULL, NULL); +- if (res != -1) return processor; ++ if (res != -1) ++ { ++ struct cpu_topology_override *override = get_cpu_topology_override(); ++ unsigned int i; ++ ++ if (!override) ++ return processor; ++ ++ for (i = 0; i < override->cpu_count; ++i) ++ if (override->host_cpu_id[i] == processor) ++ return i; ++ ++ WARN("Thread is running on processor which is not in the defined override.\n"); ++ } + #endif + + if (peb->NumberOfProcessors > 1) +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 39ab7912490..b29de19ec8e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -279,6 +279,7 @@ extern NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MA + ULONG options, void *ea_buffer, ULONG ea_length ) DECLSPEC_HIDDEN; + extern void init_files(void) DECLSPEC_HIDDEN; + extern void init_cpu_info(void) DECLSPEC_HIDDEN; ++extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) DECLSPEC_HIDDEN; + + extern void dbg_init(void) DECLSPEC_HIDDEN; + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index f4f45d958be..aed7090572b 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -794,6 +794,12 @@ typedef struct + lparam_t info; + } cursor_pos_t; + ++struct cpu_topology_override ++{ ++ unsigned int cpu_count; ++ unsigned char host_cpu_id[64]; ++}; ++ + + + +@@ -895,6 +901,7 @@ struct init_process_done_request + mod_handle_t module; + client_ptr_t ldt_copy; + client_ptr_t entry; ++ /* VARARG(cpu_override,cpu_topology_override); */ + }; + struct init_process_done_reply + { +diff --git a/server/process.c b/server/process.c +index 97e074a5261..d1f9ab764f7 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -73,6 +73,7 @@ static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); ++static void set_process_affinity( struct process *process, affinity_t affinity ); + + static const struct object_ops process_ops = + { +@@ -550,6 +551,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_kbd = NULL; + process->esync_fd = -1; + process->fsync_idx = 0; ++ process->cpu_override.cpu_count = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -1368,6 +1370,8 @@ DECL_HANDLER(init_process_done) + { + struct process_dll *dll; + struct process *process = current->process; ++ const struct cpu_topology_override *cpu_override = get_req_data(); ++ unsigned int have_cpu_override = get_req_data_size() / sizeof(*cpu_override); + struct memory_view *view; + client_ptr_t base; + const pe_image_info_t *image_info; +@@ -1397,6 +1401,9 @@ DECL_HANDLER(init_process_done) + if (req->gui) process->idle_event = create_event( NULL, NULL, 0, 1, 0, NULL ); + if (process->debugger) set_process_debug_flag( process, 1 ); + reply->suspend = (current->suspend || process->suspend); ++ ++ if (have_cpu_override) ++ process->cpu_override = *cpu_override; + } + + /* open a handle to a process */ +diff --git a/server/process.h b/server/process.h +index 430fd365508..d1b8b3fc222 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -100,6 +100,7 @@ struct process + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; ++ struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 48f9f4fae90..0f2fe077b80 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -810,6 +810,12 @@ typedef struct + lparam_t info; + } cursor_pos_t; + ++struct cpu_topology_override ++{ ++ unsigned int cpu_count; ++ unsigned char host_cpu_id[64]; ++}; ++ + /****************************************************************/ + /* Request declarations */ + +@@ -877,6 +883,7 @@ typedef struct + + /* Signal the end of the process initialization */ + @REQ(init_process_done) ++ VARARG(cpu_override,cpu_topology_override); /* Overridden CPUs to host CPUs mapping. */ + client_ptr_t teb; /* TEB of new thread (in process address space) */ + client_ptr_t peb; /* PEB of new process (in process address space) */ + client_ptr_t ldt_copy; /* address of LDT copy (in process address space) */ +diff --git a/server/thread.c b/server/thread.c +index 0fb3aa0e727..31043c3df33 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -605,8 +605,19 @@ int set_thread_affinity( struct thread *thread, affinity_t affinity ) + + CPU_ZERO( &set ); + for (i = 0, mask = 1; mask; i++, mask <<= 1) +- if (affinity & mask) CPU_SET( i, &set ); +- ++ if (affinity & mask) ++ { ++ if (thread->process->cpu_override.cpu_count) ++ { ++ if (i >= thread->process->cpu_override.cpu_count) ++ break; ++ CPU_SET( thread->process->cpu_override.host_cpu_id[i], &set ); ++ } ++ else ++ { ++ CPU_SET( i, &set ); ++ } ++ } + ret = sched_setaffinity( thread->unix_tid, sizeof(set), &set ); + } + #endif +@@ -1500,7 +1511,7 @@ DECL_HANDLER(init_thread) + + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); +- else ++ else if (!process->cpu_override.cpu_count) + set_thread_affinity( current, current->affinity ); + + debug_level = max( debug_level, req->debug_level ); +@@ -1514,10 +1525,12 @@ DECL_HANDLER(init_thread) + current->unix_tid = req->unix_tid; + current->teb = req->teb; + current->entry_point = req->entry; ++ struct process *process = current->process; + + init_thread_context( current ); + generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); +- set_thread_affinity( current, current->affinity ); ++ if (!process->cpu_override.cpu_count) ++ set_thread_affinity( current, current->affinity ); + + reply->pid = get_process_id( current->process ); + reply->tid = get_thread_id( current ); +diff --git a/server/trace.c b/server/trace.c +index e3a29beb6da..7df5681357b 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -1293,6 +1293,24 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size ) + fputc( '}', stderr ); + } + ++static void dump_varargs_cpu_topology_override( const char *prefix, data_size_t size ) ++{ ++ const struct cpu_topology_override *cpu_topology = cur_data; ++ unsigned int i; ++ ++ if (size < sizeof(*cpu_topology)) ++ return; ++ ++ fprintf( stderr,"%s{", prefix ); ++ for (i = 0; i < cpu_topology->cpu_count; ++i) ++ { ++ if (i) fputc( ',', stderr ); ++ fprintf( stderr, "%u", cpu_topology->host_cpu_id[i] ); ++ } ++ fputc( '}', stderr ); ++ remove_data( size ); ++} ++ + typedef void (*dump_func)( const void *req ); + + /* Everything below this line is generated automatically by tools/make_requests */ +@@ -1369,6 +1387,7 @@ static void dump_init_process_done_request( const struct init_process_done_reque + dump_uint64( ", module=", &req->module ); + dump_uint64( ", ldt_copy=", &req->ldt_copy ); + dump_uint64( ", entry=", &req->entry ); ++ dump_varargs_cpu_topology_override( ", cpu_override=", cur_size ); + } + + static void dump_init_process_done_reply( const struct init_process_done_reply *req ) + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-a5f17fa.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-a5f17fa.patch new file mode 100644 index 000000000..dbcecc6db --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-a5f17fa.patch @@ -0,0 +1,586 @@ +From e6c96f88d8f78a9890a5b96a8e85406ad9695f3c Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 2 Nov 2020 23:03:20 +0300 +Subject: [PATCH] ntdll: (HACK) Add variable to report all logical CPUs as + physical cores. + +--- + dlls/ntdll/unix/system.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c +index 0b83761ed46..bf3d6c34f14 100644 +--- a/dlls/ntdll/unix/system.c ++++ b/dlls/ntdll/unix/system.c +@@ -830,7 +830,8 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + static const char core_info[] = "/sys/devices/system/cpu/cpu%u/topology/%s"; + static const char cache_info[] = "/sys/devices/system/cpu/cpu%u/cache/index%u/%s"; + static const char numa_info[] = "/sys/devices/system/node/node%u/cpumap"; +- ++ const char *env_fake_logical_cores = getenv("WINE_LOGICAL_CPUS_AS_CORES"); ++ BOOL fake_logical_cpus_as_cores = env_fake_logical_cores && atoi(env_fake_logical_cores); + FILE *fcpu_list, *fnuma_list, *f; + DWORD len = 0, beg, end, i, j, r, num_cpus = 0, max_cpus = 0; + char op, name[MAX_PATH]; +@@ -902,7 +903,7 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + */ + /* Mask of logical threads sharing same physical core in kernel core numbering. */ + sprintf(name, core_info, i, "thread_siblings"); +- if(!sysfs_parse_bitmap(name, &thread_mask)) thread_mask = 1< +Date: Fri, 20 Nov 2020 18:01:11 +0300 +Subject: [PATCH] ntdll: Implement CPU topology override. + +--- + dlls/ntdll/unix/server.c | 3 + + dlls/ntdll/unix/system.c | 167 ++++++++++++++++++++++++++++++--- + dlls/ntdll/unix/thread.c | 15 ++- + dlls/ntdll/unix/unix_private.h | 1 + + include/wine/server_protocol.h | 9 +- + server/process.c | 7 ++ + server/process.h | 1 + + server/protocol.def | 7 ++ + server/thread.c | 20 +++- + server/trace.c | 19 ++++ + 10 files changed, 228 insertions(+), 21 deletions(-) + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index b1d3e863240..7b0c7c32f58 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1467,6 +1467,7 @@ void CDECL server_init_process_done( void *relay ) + void server_init_process_done(void) + { + void *entry, *teb; ++ struct cpu_topology_override *cpu_override = get_cpu_topology_override(); + unsigned int status; + int suspend; + +@@ -1488,6 +1489,8 @@ void CDECL server_init_process_done( void *relay ) + /* Signal the parent process to continue */ + SERVER_START_REQ( init_process_done ) + { ++ if (cpu_override) ++ wine_server_add_data( req, cpu_override, sizeof(*cpu_override) ); + req->teb = wine_server_client_ptr( teb ); + req->peb = NtCurrentTeb64() ? NtCurrentTeb64()->Peb : wine_server_client_ptr( peb ); + #ifdef __i386__ +diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c +index bf3d6c34f14..fc8165f2828 100644 +--- a/dlls/ntdll/unix/system.c ++++ b/dlls/ntdll/unix/system.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #ifdef HAVE_SYS_PARAM_H +@@ -165,6 +166,12 @@ struct smbios_boot_info + static unsigned int logical_proc_info_len, logical_proc_info_alloc_len; + static SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *logical_proc_info_ex; + static unsigned int logical_proc_info_ex_size, logical_proc_info_ex_alloc_size; ++static struct ++{ ++ struct cpu_topology_override mapping; ++ BOOL smt; ++} ++cpu_override; + + /******************************************************************************* + * Architecture specific feature detection for CPUs +@@ -475,6 +482,88 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info ) + + #endif /* End architecture specific feature detection for CPUs */ + ++static void fill_cpu_override(unsigned int host_cpu_count) ++{ ++ const char *env_override = getenv("WINE_CPU_TOPOLOGY"); ++ unsigned int i; ++ char *s; ++ ++ if (!env_override) ++ return; ++ ++ cpu_override.mapping.cpu_count = strtol(env_override, &s, 10); ++ if (s == env_override) ++ goto error; ++ ++ if (!cpu_override.mapping.cpu_count || cpu_override.mapping.cpu_count > MAXIMUM_PROCESSORS) ++ { ++ ERR("Invalid logical CPU count %u, limit %u.\n", cpu_override.mapping.cpu_count, MAXIMUM_PROCESSORS); ++ goto error; ++ } ++ ++ if (tolower(*s) == 's') ++ { ++ cpu_override.mapping.cpu_count *= 2; ++ if (cpu_override.mapping.cpu_count > MAXIMUM_PROCESSORS) ++ { ++ ERR("Logical CPU count exceeds limit %u.\n", MAXIMUM_PROCESSORS); ++ goto error; ++ } ++ cpu_override.smt = TRUE; ++ ++s; ++ } ++ if (*s != ':') ++ goto error; ++ ++s; ++ for (i = 0; i < cpu_override.mapping.cpu_count; ++i) ++ { ++ char *next; ++ ++ if (i) ++ { ++ if (*s != ',') ++ { ++ if (!*s) ++ ERR("Incomplete host CPU mapping string, %u CPUs mapping required.\n", ++ cpu_override.mapping.cpu_count); ++ goto error; ++ } ++ ++s; ++ } ++ ++ cpu_override.mapping.host_cpu_id[i] = strtol(s, &next, 10); ++ if (next == s) ++ goto error; ++ if (cpu_override.mapping.host_cpu_id[i] >= host_cpu_count) ++ { ++ ERR("Invalid host CPU index %u (host_cpu_count %u).\n", ++ cpu_override.mapping.host_cpu_id[i], host_cpu_count); ++ goto error; ++ } ++ s = next; ++ } ++ if (*s) ++ goto error; ++ ++ ERR("Overriding CPU configuration, %u logical CPUs, host CPUs ", cpu_override.mapping.cpu_count); ++ for (i = 0; i < cpu_override.mapping.cpu_count; ++i) ++ { ++ if (i) ++ ERR(","); ++ ERR("%u", cpu_override.mapping.host_cpu_id[i]); ++ } ++ ERR("\n"); ++ return; ++error: ++ cpu_override.mapping.cpu_count = 0; ++ ERR("Invalid WINE_CPU_TOPOLOGY string %s (%s).\n", debugstr_a(env_override), debugstr_a(s)); ++} ++ ++struct cpu_topology_override *get_cpu_topology_override(void) ++{ ++ return cpu_override.mapping.cpu_count ? &cpu_override.mapping : NULL; ++} ++ + static BOOL grow_logical_proc_buf(void) + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *new_data; +@@ -942,6 +942,12 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + if (op == '-') fscanf(fcpu_list, "%u%c ", &end, &op); + else end = beg; + ++ if (cpu_override.mapping.cpu_count) ++ { ++ beg = 0; ++ end = cpu_override.mapping.cpu_count - 1; ++ } ++ + for(i = beg; i <= end; i++) + { + DWORD phys_core = 0; +@@ -956,7 +956,9 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + continue; + } + +- sprintf(name, core_info, i, "physical_package_id"); ++ sprintf(name, core_info, cpu_override.mapping.cpu_count ? cpu_override.mapping.host_cpu_id[i] : i, ++ "physical_package_id"); ++ + f = fopen(name, "r"); + if (f) + { +@@ -981,19 +981,34 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * + + /* Mask of logical threads sharing same physical core in kernel core numbering. */ + sprintf(name, core_info, i, "thread_siblings"); +- if(fake_logical_cpus_as_cores || !sysfs_parse_bitmap(name, &thread_mask)) thread_mask = (ULONG_PTR)1<NumberOfProcessors = num; ++ ++ fill_cpu_override(num); ++ ++ peb->NumberOfProcessors = cpu_override.mapping.cpu_count ++ ? cpu_override.mapping.cpu_count : num; + get_cpuinfo( &cpu_info ); + TRACE( "<- CPU arch %d, level %d, rev %d, features 0x%x\n", + cpu_info.Architecture, cpu_info.Level, cpu_info.Revision, cpu_info.FeatureSet ); +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index ca5dac43bb0..bc6a5451611 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1323,7 +1323,20 @@ ULONG WINAPI NtGetCurrentProcessorNumber(void) + + #if defined(__linux__) && defined(__NR_getcpu) + int res = syscall(__NR_getcpu, &processor, NULL, NULL); +- if (res != -1) return processor; ++ if (res != -1) ++ { ++ struct cpu_topology_override *override = get_cpu_topology_override(); ++ unsigned int i; ++ ++ if (!override) ++ return processor; ++ ++ for (i = 0; i < override->cpu_count; ++i) ++ if (override->host_cpu_id[i] == processor) ++ return i; ++ ++ WARN("Thread is running on processor which is not in the defined override.\n"); ++ } + #endif + + if (peb->NumberOfProcessors > 1) +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 39ab7912490..b29de19ec8e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -279,6 +279,7 @@ extern NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MA + ULONG options, void *ea_buffer, ULONG ea_length ) DECLSPEC_HIDDEN; + extern void init_files(void) DECLSPEC_HIDDEN; + extern void init_cpu_info(void) DECLSPEC_HIDDEN; ++extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) DECLSPEC_HIDDEN; + + extern void dbg_init(void) DECLSPEC_HIDDEN; + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index f4f45d958be..aed7090572b 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -794,6 +794,12 @@ typedef struct + lparam_t info; + } cursor_pos_t; + ++struct cpu_topology_override ++{ ++ unsigned int cpu_count; ++ unsigned char host_cpu_id[64]; ++}; ++ + + + +@@ -895,6 +901,7 @@ struct init_process_done_request + mod_handle_t module; + client_ptr_t ldt_copy; + client_ptr_t entry; ++ /* VARARG(cpu_override,cpu_topology_override); */ + }; + struct init_process_done_reply + { +diff --git a/server/process.c b/server/process.c +index 97e074a5261..d1f9ab764f7 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -73,6 +73,7 @@ static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); ++static void set_process_affinity( struct process *process, affinity_t affinity ); + + static const struct object_ops process_ops = + { +@@ -550,6 +551,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_kbd = NULL; + process->esync_fd = -1; + process->fsync_idx = 0; ++ process->cpu_override.cpu_count = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -1368,6 +1370,8 @@ DECL_HANDLER(init_process_done) + { + struct process_dll *dll; + struct process *process = current->process; ++ const struct cpu_topology_override *cpu_override = get_req_data(); ++ unsigned int have_cpu_override = get_req_data_size() / sizeof(*cpu_override); + struct memory_view *view; + client_ptr_t base; + const pe_image_info_t *image_info; +@@ -1397,6 +1401,9 @@ DECL_HANDLER(init_process_done) + if (req->gui) process->idle_event = create_event( NULL, NULL, 0, 1, 0, NULL ); + if (process->debugger) set_process_debug_flag( process, 1 ); + reply->suspend = (current->suspend || process->suspend); ++ ++ if (have_cpu_override) ++ process->cpu_override = *cpu_override; + } + + /* open a handle to a process */ +diff --git a/server/process.h b/server/process.h +index 430fd365508..d1b8b3fc222 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -100,6 +100,7 @@ struct process + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ + unsigned int fsync_idx; ++ struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 48f9f4fae90..0f2fe077b80 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -810,6 +810,12 @@ typedef struct + lparam_t info; + } cursor_pos_t; + ++struct cpu_topology_override ++{ ++ unsigned int cpu_count; ++ unsigned char host_cpu_id[64]; ++}; ++ + /****************************************************************/ + /* Request declarations */ + +@@ -877,6 +883,7 @@ typedef struct + + /* Signal the end of the process initialization */ + @REQ(init_process_done) ++ VARARG(cpu_override,cpu_topology_override); /* Overridden CPUs to host CPUs mapping. */ + client_ptr_t teb; /* TEB of new thread (in process address space) */ + client_ptr_t peb; /* PEB of new process (in process address space) */ + client_ptr_t ldt_copy; /* address of LDT copy (in process address space) */ +diff --git a/server/thread.c b/server/thread.c +index 0fb3aa0e727..31043c3df33 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -605,8 +605,19 @@ int set_thread_affinity( struct thread *thread, affinity_t affinity ) + + CPU_ZERO( &set ); + for (i = 0, mask = 1; mask; i++, mask <<= 1) +- if (affinity & mask) CPU_SET( i, &set ); +- ++ if (affinity & mask) ++ { ++ if (thread->process->cpu_override.cpu_count) ++ { ++ if (i >= thread->process->cpu_override.cpu_count) ++ break; ++ CPU_SET( thread->process->cpu_override.host_cpu_id[i], &set ); ++ } ++ else ++ { ++ CPU_SET( i, &set ); ++ } ++ } + ret = sched_setaffinity( thread->unix_tid, sizeof(set), &set ); + } + #endif +@@ -1500,7 +1511,7 @@ DECL_HANDLER(init_thread) + + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); +- else ++ else if (!process->cpu_override.cpu_count) + set_thread_affinity( current, current->affinity ); + + debug_level = max( debug_level, req->debug_level ); +@@ -1514,10 +1525,12 @@ DECL_HANDLER(init_thread) + current->unix_tid = req->unix_tid; + current->teb = req->teb; + current->entry_point = req->entry; ++ struct process *process = current->process; + + init_thread_context( current ); + generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); +- set_thread_affinity( current, current->affinity ); ++ if (!process->cpu_override.cpu_count) ++ set_thread_affinity( current, current->affinity ); + + reply->pid = get_process_id( current->process ); + reply->tid = get_thread_id( current ); +diff --git a/server/trace.c b/server/trace.c +index e3a29beb6da..7df5681357b 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -1293,6 +1293,24 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size ) + fputc( '}', stderr ); + } + ++static void dump_varargs_cpu_topology_override( const char *prefix, data_size_t size ) ++{ ++ const struct cpu_topology_override *cpu_topology = cur_data; ++ unsigned int i; ++ ++ if (size < sizeof(*cpu_topology)) ++ return; ++ ++ fprintf( stderr,"%s{", prefix ); ++ for (i = 0; i < cpu_topology->cpu_count; ++i) ++ { ++ if (i) fputc( ',', stderr ); ++ fprintf( stderr, "%u", cpu_topology->host_cpu_id[i] ); ++ } ++ fputc( '}', stderr ); ++ remove_data( size ); ++} ++ + typedef void (*dump_func)( const void *req ); + + /* Everything below this line is generated automatically by tools/make_requests */ +@@ -1369,6 +1387,7 @@ static void dump_init_process_done_request( const struct init_process_done_reque + dump_uint64( ", module=", &req->module ); + dump_uint64( ", ldt_copy=", &req->ldt_copy ); + dump_uint64( ", entry=", &req->entry ); ++ dump_varargs_cpu_topology_override( ", cpu_override=", cur_size ); + } + + static void dump_init_process_done_reply( const struct init_process_done_reply *req ) + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides index 6d3ac41b9..884bbe80c 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides @@ -2,8 +2,12 @@ # Proton CPU topology override - depends on protonify and fsync if ( [ "$_staging_esync" = "true" ] || [ "$_use_esync" = "true" ] ) && [ "$_use_fsync" = "true" ] && [ "$_protonify" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 6f158754435f403864052e595ab627dadac2666f HEAD ); then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then _patchname='proton-cpu-topology-overrides.patch' && _patchmsg="Enable Proton's CPU topology override support" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor a5f17fac3ee7827d5e8e6878c622084e9ad776e0 HEAD ); then + _patchname='proton-cpu-topology-overrides-59485f0.patch' && _patchmsg="Enable Proton's CPU topology override support" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + _patchname='proton-cpu-topology-overrides-a5f17fa.patch' && _patchmsg="Enable Proton's CPU topology override support" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 05676e83e975816b3c0d87b4b18b5cd15a372489 HEAD ); then _patchname='proton-cpu-topology-overrides-af4378d.patch' && _patchmsg="Enable Proton's CPU topology override support" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor b3ca48f39ce822c197193ffc419771f1869f3c83 HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides.patch index dbcecc6db..810617afa 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/proton-cpu-topology-overrides.patch @@ -25,7 +25,7 @@ index 0b83761ed46..bf3d6c34f14 100644 @@ -902,7 +903,7 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION * */ /* Mask of logical threads sharing same physical core in kernel core numbering. */ - sprintf(name, core_info, i, "thread_siblings"); + snprintf(name, sizeof(name), core_info, i, "thread_siblings"); - if(!sysfs_parse_bitmap(name, &thread_mask)) thread_mask = 1< +Date: Tue, 6 Aug 2019 13:27:25 -0500 +Subject: [PATCH] winebus.sys: Disable UDEV lnxev devices by default. + +Based on a patch from Simon McVittie . +--- + dlls/winebus.sys/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c +index 111953c3bae..2a583871f1a 100644 +--- a/dlls/winebus.sys/main.c ++++ b/dlls/winebus.sys/main.c +@@ -742,7 +742,7 @@ static NTSTATUS udev_driver_init(void) + + bus_options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); + if (bus_options.disable_hidraw) TRACE("UDEV hidraw devices disabled in registry\n"); +- bus_options.disable_input = check_bus_option(L"DisableInput", 0); ++ bus_options.disable_input = check_bus_option(L"DisableInput", 1); + if (bus_options.disable_input) TRACE("UDEV input devices disabled in registry\n"); + bus_options.disable_udevd = check_bus_option(L"DisableUdevd", 0); + if (bus_options.disable_udevd) TRACE("UDEV udevd use disabled in registry\n"); +From 22d2f82678b9babe0a315df4f311221e999e7fbc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 30 Sep 2021 15:07:25 +0200 +Subject: [PATCH] HACK: winebus.sys: Prefer devices on UDEV hidraw bus over SDL + bus. + +--- + dlls/winebus.sys/main.c | 42 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 33 insertions(+), 9 deletions(-) + +diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c +index 2a583871f1a..1af7b5070f6 100644 +--- a/dlls/winebus.sys/main.c ++++ b/dlls/winebus.sys/main.c +@@ -71,6 +71,7 @@ struct device_extension + { + struct list entry; + DEVICE_OBJECT *device; ++ const WCHAR *bus_name; + + CRITICAL_SECTION cs; + enum device_state state; +@@ -284,7 +285,7 @@ static void remove_pending_irps(DEVICE_OBJECT *device) + } + } + +-static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, UINT64 unix_device) ++static DEVICE_OBJECT *bus_create_hid_device(const WCHAR *bus_name, struct device_desc *desc, UINT64 unix_device) + { + struct device_extension *ext; + DEVICE_OBJECT *device; +@@ -307,6 +308,7 @@ static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, struct uni + + /* fill out device_extension struct */ + ext = (struct device_extension *)device->DeviceExtension; ++ ext->bus_name = bus_name; + ext->device = device; + ext->desc = *desc; + ext->index = get_device_index(desc); +@@ -333,6 +335,17 @@ static DEVICE_OBJECT *bus_find_unix_device(struct unix_device *unix_device) + return NULL; + } + ++static DEVICE_OBJECT *bus_find_device_from_vid_pid(const WCHAR *bus_name, struct device_desc *desc) ++{ ++ struct device_extension *ext; ++ ++ LIST_FOR_EACH_ENTRY(ext, &device_list, struct device_extension, entry) ++ if (!wcscmp(ext->bus_name, bus_name) && ext->desc.vid == desc->vid && ++ ext->desc.pid == desc->pid) return ext->device; ++ ++ return NULL; ++} ++ + static void bus_unlink_hid_device(DEVICE_OBJECT *device) + { + struct device_extension *ext = (struct device_extension *)device->DeviceExtension; +@@ -519,7 +532,7 @@ static void mouse_device_create(void) + struct device_create_params params = {{0}}; + + if (winebus_call(mouse_create, ¶ms)) return; +- mouse_obj = bus_create_hid_device(¶ms.desc, params.device); ++ mouse_obj = bus_create_hid_device(L"WINEBUS", ¶ms.desc, params.device); + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + } + +@@ -528,7 +541,7 @@ static void keyboard_device_create(void) + struct device_create_params params = {{0}}; + + if (winebus_call(keyboard_create, ¶ms)) return; +- keyboard_obj = bus_create_hid_device(¶ms.desc, params.device); ++ keyboard_obj = bus_create_hid_device(L"WINEBUS", ¶ms.desc, params.device); + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + } + +@@ -574,7 +587,20 @@ static DWORD CALLBACK bus_main_thread(void *args) + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + break; + case BUS_EVENT_TYPE_DEVICE_CREATED: +- device = bus_create_hid_device(&event->device_created.desc, event->device); ++ RtlEnterCriticalSection(&device_list_cs); ++ if (!wcscmp(bus.name, L"SDL")) ++ { ++ if (bus_find_device_from_vid_pid(L"UDEV", &event->device_created.desc)) device = NULL; ++ else device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ } ++ else if (!wcscmp(bus.name, L"UDEV")) ++ { ++ if ((device = bus_find_device_from_vid_pid(L"SDL", &event->device_created.desc))) ++ bus_unlink_hid_device(device); ++ device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ } ++ else device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ RtlLeaveCriticalSection(&device_list_cs); + if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); + else + { +@@ -778,11 +804,9 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) + mouse_device_create(); + keyboard_device_create(); + +- if (!check_bus_option(L"Enable SDL", 1) || sdl_driver_init()) +- { +- udev_driver_init(); +- iohid_driver_init(); +- } ++ udev_driver_init(); ++ iohid_driver_init(); ++ sdl_driver_init(); + + irp->IoStatus.Status = STATUS_SUCCESS; + break; +From f33bc009edb3349d6b3302cbe9de994926a3a4c3 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 6 Aug 2019 13:37:38 -0500 +Subject: [PATCH] HACK: winebus.sys: Don't use hidraw for XBox controllers. + +--- + dlls/winebus.sys/bus_udev.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 2269f5b904e..334373f82e6 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1287,7 +1287,12 @@ static void udev_add_device(struct udev_device *dev, int fd) + } + + if (is_xbox_gamepad(desc.vid, desc.pid)) +- desc.is_gamepad = TRUE; ++ { ++ /* SDL handles xbox (and steam) controllers */ ++ TRACE("hidraw %s: ignoring %s, xbox gamepad\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + #ifdef HAS_PROPER_INPUT_HEADER + else + { +From 9d9d0ea6f5041b6f10a3ee031d2b0e72818841cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 30 Sep 2021 15:28:03 +0200 +Subject: [PATCH] HACK: winebus.sys: Don't use hidraw for Steam controllers. + +--- + dlls/winebus.sys/bus_udev.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 13 +++++++++++++ + 3 files changed, 21 insertions(+) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 334373f82e6..4d1124f5cc4 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1286,6 +1286,13 @@ static void udev_add_device(struct udev_device *dev, int fd) + memcpy(desc.serialnumber, zeros, sizeof(zeros)); + } + ++ if (is_steam_controller(desc.vid, desc.pid)) ++ { ++ /* this device is being used as a virtual Steam controller */ ++ TRACE("hidraw %s: ignoring %s, steam controller\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + if (is_xbox_gamepad(desc.vid, desc.pid)) + { + /* SDL handles xbox (and steam) controllers */ +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index efecf6cdbe3..6441f987a95 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface); + + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags); + ++BOOL is_steam_controller(WORD vid, WORD pid); + BOOL is_xbox_gamepad(WORD vid, WORD pid); + BOOL is_dualshock4_gamepad(WORD vid, WORD pid); + +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index 1269ae05c2b..e88d2181446 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -38,6 +38,19 @@ + + #include "unix_private.h" + ++BOOL is_steam_controller(WORD vid, WORD pid) ++{ ++ if (vid != 0x28de) return FALSE; ++ if (pid == 0x1101) return TRUE; /* Valve Legacy Steam Controller */ ++ if (pid == 0x1102) return TRUE; /* Valve wired Steam Controller */ ++ if (pid == 0x1105) return TRUE; /* Valve Bluetooth Steam Controller */ ++ if (pid == 0x1106) return TRUE; /* Valve Bluetooth Steam Controller */ ++ if (pid == 0x1142) return TRUE; /* Valve wireless Steam Controller */ ++ if (pid == 0x1201) return TRUE; /* Valve wired Steam Controller */ ++ if (pid == 0x1202) return TRUE; /* Valve Bluetooth Steam Controller */ ++ return FALSE; ++} ++ + BOOL is_xbox_gamepad(WORD vid, WORD pid) + { + if (vid != 0x045e) return FALSE; +From 1cf8f159e76931d5e4c975e1a659bf8867fa8034 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 7 Oct 2021 15:29:05 +0200 +Subject: [PATCH] HACK: winebus.sys: Ignore blacklisted SDL controllers and + joysticks. + +--- + dlls/winebus.sys/bus_sdl.c | 27 +++++++++++++++++---------- + dlls/winebus.sys/bus_udev.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 18 ++++++++++++++++++ + 4 files changed, 43 insertions(+), 10 deletions(-) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 37d75d0bc95..7dfa8edb3b5 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -862,16 +862,8 @@ static void sdl_add_device(unsigned int index) + return; + } + +- if (options.map_controllers && pSDL_IsGameController(index)) +- controller = pSDL_GameControllerOpen(index); +- +- if (controller) product = pSDL_GameControllerName(controller); +- else product = pSDL_JoystickName(joystick); +- if (!product) product = "Joystick"; +- +- id = pSDL_JoystickInstanceID(joystick); +- +- if (pSDL_JoystickGetProductVersion != NULL) { ++ if (pSDL_JoystickGetProductVersion != NULL) ++ { + desc.vid = pSDL_JoystickGetVendor(joystick); + desc.pid = pSDL_JoystickGetProduct(joystick); + desc.version = pSDL_JoystickGetProductVersion(joystick); +@@ -883,6 +875,22 @@ static void sdl_add_device(unsigned int index) + desc.version = 0; + } + ++ if (is_sdl_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("ignoring %s, in SDL blacklist\n", debugstr_device_desc(&desc)); ++ return; ++ } ++ ++ if (options.map_controllers && pSDL_IsGameController(index)) ++ controller = pSDL_GameControllerOpen(index); ++ ++ if (controller) product = pSDL_GameControllerName(controller); ++ else product = pSDL_JoystickName(joystick); ++ if (!product) product = "Joystick"; ++ ++ id = pSDL_JoystickInstanceID(joystick); ++ + if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) + { + ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 4d1124f5cc4..3a525b235c6 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1286,6 +1286,13 @@ static void udev_add_device(struct udev_device *dev, int fd) + memcpy(desc.serialnumber, zeros, sizeof(zeros)); + } + ++ if (is_sdl_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("hidraw %s: ignoring %s, in SDL blacklist\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + if (is_steam_controller(desc.vid, desc.pid)) + { + /* this device is being used as a virtual Steam controller */ +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index 6441f987a95..02c9ff947c4 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface); + + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags); + ++BOOL is_sdl_blacklisted(DWORD vid, DWORD pid); + BOOL is_steam_controller(WORD vid, WORD pid); + BOOL is_xbox_gamepad(WORD vid, WORD pid); + BOOL is_dualshock4_gamepad(WORD vid, WORD pid); +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index e88d2181446..8cff984ac20 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -38,6 +38,24 @@ + + #include "unix_private.h" + ++/* logic from SDL2's SDL_ShouldIgnoreGameController */ ++BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) ++{ ++ const char *allow_virtual = getenv("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD"); ++ const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"); ++ const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES"); ++ char needle[16]; ++ ++ if (vid == 0x28de && pid == 0x11ff && allow_virtual && *allow_virtual && ++ *allow_virtual != '0' && strcasecmp(allow_virtual, "false")) ++ return FALSE; ++ ++ sprintf(needle, "0x%04x/0x%04x", vid, pid); ++ if (whitelist) return strcasestr(whitelist, needle) == NULL; ++ if (blacklist) return strcasestr(blacklist, needle) != NULL; ++ return FALSE; ++} ++ + BOOL is_steam_controller(WORD vid, WORD pid) + { + if (vid != 0x28de) return FALSE; +From 7ea1cc2581b35b6d630d0399a0e230b3f57a2014 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 30 Aug 2019 10:20:16 -0500 +Subject: [PATCH] HACK: winebus.sys: Override Steam virtual controller vid/pid + with Xbox. + +Matches Windows Steam client behavior. +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 7dfa8edb3b5..079a615333e 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -882,6 +882,13 @@ static void sdl_add_device(unsigned int index) + return; + } + ++ if (desc.vid == 0x28de && desc.pid == 0x11ff) ++ { ++ TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); ++ desc.vid = 0x045e; ++ desc.pid = 0x028e; ++ } ++ + if (options.map_controllers && pSDL_IsGameController(index)) + controller = pSDL_GameControllerOpen(index); + +From e62a27bb284c4a7364d69fb09ae4df18c8860185 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 30 Sep 2021 20:38:04 +0200 +Subject: [PATCH] winebus.sys: Ignore some joysticks that SDL reports. + +SDL has a blacklist, but it isn't complete. Ignore some more devices +while we fix upstream. +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 14 ++++++++++++++ + include/wine/js_blacklist.h | 3 +++ + 4 files changed, 25 insertions(+) + create mode 100644 include/wine/js_blacklist.h + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 079a615333e..84c8721270c 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -882,6 +882,13 @@ static void sdl_add_device(unsigned int index) + return; + } + ++ if (is_wine_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("ignoring %s, in Wine blacklist\n", debugstr_device_desc(&desc)); ++ return; ++ } ++ + if (desc.vid == 0x28de && desc.pid == 0x11ff) + { + TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index 02c9ff947c4..a204eacb3bf 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -265,6 +265,7 @@ extern void hid_device_drop_report(struct unix_device *iface); + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags); + + BOOL is_sdl_blacklisted(DWORD vid, DWORD pid); ++BOOL is_wine_blacklisted(DWORD vid, DWORD pid); + BOOL is_steam_controller(WORD vid, WORD pid); + BOOL is_xbox_gamepad(WORD vid, WORD pid); + BOOL is_dualshock4_gamepad(WORD vid, WORD pid); +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index 8cff984ac20..ae742b0cba4 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -35,9 +35,23 @@ + #include "wine/debug.h" + #include "wine/list.h" + #include "wine/unixlib.h" ++#include "wine/js_blacklist.h" /* for wine_js_blacklist */ + + #include "unix_private.h" + ++BOOL is_wine_blacklisted(DWORD vid, DWORD pid) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(wine_js_blacklist); ++i) ++ { ++ if (vid != wine_js_blacklist[i].vid) continue; ++ if (!wine_js_blacklist[i].pid || wine_js_blacklist[i].pid == pid) return TRUE; ++ } ++ ++ return FALSE; ++} ++ + /* logic from SDL2's SDL_ShouldIgnoreGameController */ + BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) + { +diff --git a/include/wine/js_blacklist.h b/include/wine/js_blacklist.h +new file mode 100644 +index 00000000000..b8f2ec7dd28 +--- /dev/null ++++ b/include/wine/js_blacklist.h +@@ -0,0 +1,3 @@ ++static const struct { short vid; short pid; } wine_js_blacklist[] = { ++ {0x056a, 0x0000}, /* all Wacom devices */ ++}; +From 62f3031decd2500dbfc48c767cc662e812c1cf78 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 10 Nov 2020 18:32:28 +0000 +Subject: [PATCH] winebus.sys: Automatically bypass udevd in Flatpak or + pressure-vessel. + +Flatpak uses unprivileged containers that don't normally map uid 0 +into the container, so netlink events won't work there, as described +in previous commits. Steam's pressure-vessel container tool behaves +similarly. +--- + dlls/winebus.sys/bus_udev.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 3a525b235c6..5d8d2384e69 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1708,6 +1708,12 @@ NTSTATUS udev_bus_init(void *args) + goto error; + } + ++ if (access("/run/pressure-vessel", R_OK) || access("/.flatpak-info", R_OK)) ++ { ++ TRACE("Container detected, bypassing udevd by default\n"); ++ options.disable_udevd = TRUE; ++ } ++ + #ifdef HAVE_SYS_INOTIFY_H + if (options.disable_udevd) monitor_fd = create_inotify(); + if (monitor_fd < 0) options.disable_udevd = FALSE; +From dd54394cee8090d2ef55c0da2ecde7a724c6a8c6 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 10 Nov 2020 19:03:47 +0000 +Subject: [PATCH] winebus.sys: Guess the type of evdev input devices. + +Ordinarily, we can get the type of an evdev input device from udev: +the input_id builtin sets udev properties of the form ID_INPUT_FOO +that we can read. + +However, in a container there is no guarantee that the libudev in the +container will interoperate with the udevd on the host system, so we +need to be prepared to do this ourselves from first principles, using +a heuristic similar to the one in udev's input_id. + +We cannot simply copy the heuristic from udev's input_id, because its +licensing is incompatible (GPL). Instead, use a vaguely similar heuristic +that works from the same inputs and will generally produce similar results. +--- + dlls/winebus.sys/bus_udev.c | 330 +++++++++++++++++++++++++++++++++++- + 1 file changed, 328 insertions(+), 2 deletions(-) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 5d8d2384e69..4592b095ff5 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -25,6 +25,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -500,7 +501,303 @@ static struct base_device *find_device_from_syspath(const char *path) + + #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7))) + +-static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) ++/* Minimal compatibility with code taken from steam-runtime-tools */ ++typedef int gboolean; ++#define g_debug(fmt, ...) TRACE(fmt "\n", ## __VA_ARGS__) ++#define G_N_ELEMENTS(arr) (sizeof(arr)/sizeof(arr[0])) ++ ++typedef enum ++{ ++ SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK = (1 << 0), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER = (1 << 1), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD = (1 << 2), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS = (1 << 3), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE = (1 << 4), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD = (1 << 5), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN = (1 << 6), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET = (1 << 7), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET_PAD = (1 << 8), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK = (1 << 9), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_SWITCH = (1 << 10), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_NONE = 0 ++} SrtInputDeviceTypeFlags; ++ ++#define BITS_PER_LONG (sizeof (unsigned long) * CHAR_BIT) ++#define LONGS_FOR_BITS(x) ((((x)-1)/BITS_PER_LONG)+1) ++typedef struct ++{ ++ unsigned long ev[LONGS_FOR_BITS (EV_MAX)]; ++ unsigned long keys[LONGS_FOR_BITS (KEY_MAX)]; ++ unsigned long abs[LONGS_FOR_BITS (ABS_MAX)]; ++ unsigned long rel[LONGS_FOR_BITS (REL_MAX)]; ++ unsigned long ff[LONGS_FOR_BITS (FF_MAX)]; ++ unsigned long props[LONGS_FOR_BITS (INPUT_PROP_MAX)]; ++} SrtEvdevCapabilities; ++ ++static gboolean ++_srt_get_caps_from_evdev (int fd, ++ unsigned int type, ++ unsigned long *bitmask, ++ size_t bitmask_len_longs) ++{ ++ size_t bitmask_len_bytes = bitmask_len_longs * sizeof (*bitmask); ++ ++ memset (bitmask, 0, bitmask_len_bytes); ++ ++ if (ioctl (fd, EVIOCGBIT (type, bitmask_len_bytes), bitmask) < 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static gboolean ++_srt_evdev_capabilities_set_from_evdev (SrtEvdevCapabilities *caps, ++ int fd) ++{ ++ if (_srt_get_caps_from_evdev (fd, 0, caps->ev, G_N_ELEMENTS (caps->ev))) ++ { ++ _srt_get_caps_from_evdev (fd, EV_KEY, caps->keys, G_N_ELEMENTS (caps->keys)); ++ _srt_get_caps_from_evdev (fd, EV_ABS, caps->abs, G_N_ELEMENTS (caps->abs)); ++ _srt_get_caps_from_evdev (fd, EV_REL, caps->rel, G_N_ELEMENTS (caps->rel)); ++ _srt_get_caps_from_evdev (fd, EV_FF, caps->ff, G_N_ELEMENTS (caps->ff)); ++ ioctl (fd, EVIOCGPROP (sizeof (caps->props)), caps->props); ++ return TRUE; ++ } ++ ++ memset (caps, 0, sizeof (*caps)); ++ return FALSE; ++} ++ ++#define JOYSTICK_ABS_AXES \ ++ ((1 << ABS_X) | (1 << ABS_Y) \ ++ | (1 << ABS_RX) | (1 << ABS_RY) \ ++ | (1 << ABS_THROTTLE) | (1 << ABS_RUDDER) \ ++ | (1 << ABS_WHEEL) | (1 << ABS_GAS) | (1 << ABS_BRAKE) \ ++ | (1 << ABS_HAT0X) | (1 << ABS_HAT0Y) \ ++ | (1 << ABS_HAT1X) | (1 << ABS_HAT1Y) \ ++ | (1 << ABS_HAT2X) | (1 << ABS_HAT2Y) \ ++ | (1 << ABS_HAT3X) | (1 << ABS_HAT3Y)) ++ ++static const unsigned int first_mouse_button = BTN_MOUSE; ++static const unsigned int last_mouse_button = BTN_JOYSTICK - 1; ++ ++static const unsigned int first_joystick_button = BTN_JOYSTICK; ++static const unsigned int last_joystick_button = BTN_GAMEPAD - 1; ++ ++static const unsigned int first_gamepad_button = BTN_GAMEPAD; ++static const unsigned int last_gamepad_button = BTN_DIGI - 1; ++ ++static const unsigned int first_dpad_button = BTN_DPAD_UP; ++static const unsigned int last_dpad_button = BTN_DPAD_RIGHT; ++ ++static const unsigned int first_extra_joystick_button = BTN_TRIGGER_HAPPY; ++static const unsigned int last_extra_joystick_button = BTN_TRIGGER_HAPPY40; ++ ++SrtInputDeviceTypeFlags ++_srt_evdev_capabilities_guess_type (const SrtEvdevCapabilities *caps) ++{ ++ SrtInputDeviceTypeFlags flags = SRT_INPUT_DEVICE_TYPE_FLAGS_NONE; ++ unsigned int i; ++ gboolean has_joystick_axes = FALSE; ++ gboolean has_joystick_buttons = FALSE; ++ ++ /* Some properties let us be fairly sure about a device */ ++ if (test_bit (caps->props, INPUT_PROP_ACCELEROMETER)) ++ { ++ g_debug ("INPUT_PROP_ACCELEROMETER => is accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ ++ if (test_bit (caps->props, INPUT_PROP_POINTING_STICK)) ++ { ++ g_debug ("INPUT_PROP_POINTING_STICK => is pointing stick"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK; ++ } ++ ++ if (test_bit (caps->props, INPUT_PROP_BUTTONPAD) ++ || test_bit (caps->props, INPUT_PROP_TOPBUTTONPAD)) ++ { ++ g_debug ("INPUT_PROP_[TOP]BUTTONPAD => is touchpad"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD; ++ } ++ ++ /* Devices with a stylus or pen are assumed to be graphics tablets */ ++ if (test_bit (caps->keys, BTN_STYLUS) ++ || test_bit (caps->keys, BTN_TOOL_PEN)) ++ { ++ g_debug ("Stylus or pen => is tablet"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET; ++ } ++ ++ /* Devices that accept a finger touch are assumed to be touchpads or ++ * touchscreens. ++ * ++ * In Steam we mostly only care about these as a way to ++ * reject non-joysticks, so we're not very precise here yet. ++ * ++ * SDL assumes that TOUCH means a touchscreen and FINGER ++ * means a touchpad. */ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE ++ && (test_bit (caps->keys, BTN_TOOL_FINGER) ++ || test_bit (caps->keys, BTN_TOUCH) ++ || test_bit (caps->props, INPUT_PROP_SEMI_MT))) ++ { ++ g_debug ("Finger or touch or semi-MT => is touchpad or touchscreen"); ++ ++ if (test_bit (caps->props, INPUT_PROP_POINTER)) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD; ++ else ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN; ++ } ++ ++ /* Devices with mouse buttons are ... probably mice? */ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE) ++ { ++ for (i = first_mouse_button; i <= last_mouse_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ { ++ g_debug ("Mouse button => mouse"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE; ++ } ++ } ++ } ++ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE) ++ { ++ for (i = ABS_X; i < ABS_Z; i++) ++ { ++ if (!test_bit (caps->abs, i)) ++ break; ++ } ++ ++ /* If it has 3 axes and no buttons it's probably an accelerometer. */ ++ if (i == ABS_Z && !test_bit (caps->ev, EV_KEY)) ++ { ++ g_debug ("3 left axes and no buttons => accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ ++ /* Same for RX..RZ (e.g. Wiimote) */ ++ for (i = ABS_RX; i < ABS_RZ; i++) ++ { ++ if (!test_bit (caps->abs, i)) ++ break; ++ } ++ ++ if (i == ABS_RZ && !test_bit (caps->ev, EV_KEY)) ++ { ++ g_debug ("3 right axes and no buttons => accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ } ++ ++ /* Bits 1 to 31 are ESC, numbers and Q to D, which SDL and udev both ++ * consider to be enough to count as a fully-functioned keyboard. */ ++ if ((caps->keys[0] & 0xfffffffe) == 0xfffffffe) ++ { ++ g_debug ("First few keys => keyboard"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD; ++ } ++ ++ /* If we have *any* keys, consider it to be something a bit ++ * keyboard-like. Bits 0 to 63 are all keyboard keys. ++ * Make sure we stop before reaching KEY_UP which is sometimes ++ * used on game controller mappings, e.g. for the Wiimote. */ ++ for (i = 0; i < (64 / BITS_PER_LONG); i++) ++ { ++ if (caps->keys[i] != 0) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS; ++ } ++ ++ if (caps->abs[0] & JOYSTICK_ABS_AXES) ++ has_joystick_axes = TRUE; ++ ++ /* Flight stick buttons */ ++ for (i = first_joystick_button; i <= last_joystick_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Gamepad buttons (Xbox, PS3, etc.) */ ++ for (i = first_gamepad_button; i <= last_gamepad_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Gamepad digital dpad */ ++ for (i = first_dpad_button; i <= last_dpad_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Steering wheel gear-change buttons */ ++ for (i = BTN_GEAR_DOWN; i <= BTN_GEAR_UP; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Reserved space for extra game-controller buttons, e.g. on Corsair ++ * gaming keyboards */ ++ for (i = first_extra_joystick_button; i <= last_extra_joystick_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ if (test_bit (caps->keys, last_mouse_button)) ++ { ++ /* Mice with a very large number of buttons can apparently ++ * overflow into the joystick-button space, but they're still not ++ * joysticks. */ ++ has_joystick_buttons = FALSE; ++ } ++ ++ /* TODO: Do we want to consider BTN_0 up to BTN_9 to be joystick buttons? ++ * libmanette and SDL look for BTN_1, udev does not. ++ * ++ * They're used by some game controllers, like BTN_1 and BTN_2 for the ++ * Wiimote, BTN_1..BTN_9 for the SpaceTec SpaceBall and BTN_0..BTN_3 ++ * for Playstation dance pads, but they're also used by ++ * non-game-controllers like Logitech mice. For now we entirely ignore ++ * these buttons: they are not evidence that it's a joystick, but ++ * neither are they evidence that it *isn't* a joystick. */ ++ ++ /* We consider it to be a joystick if there is some evidence that it is, ++ * and no evidence that it's something else. ++ * ++ * Unlike SDL, we accept devices with only axes and no buttons as a ++ * possible joystick, unless they have X/Y/Z axes in which case we ++ * assume they're accelerometers. */ ++ if ((has_joystick_buttons || has_joystick_axes) ++ && (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE)) ++ { ++ g_debug ("Looks like a joystick"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK; ++ } ++ ++ /* If we have *any* keys below BTN_MISC, consider it to be something ++ * a bit keyboard-like, but don't rule out *also* being considered ++ * to be a joystick (again for e.g. the Wiimote). */ ++ for (i = 0; i < (BTN_MISC / BITS_PER_LONG); i++) ++ { ++ if (caps->keys[i] != 0) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS; ++ } ++ ++ /* Also non-exclusive: don't rule out a device being a joystick and ++ * having a switch */ ++ if (test_bit (caps->ev, EV_SW)) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_SWITCH; ++ ++ return flags; ++} ++ ++static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) + { + static const USAGE_AND_PAGE Unknown = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = 0}; + static const USAGE_AND_PAGE Mouse = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_MOUSE}; +@@ -510,6 +807,7 @@ static const BYTE* what_am_I(struct udev_device *dev) + static const USAGE_AND_PAGE Tablet = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_PEN}; + static const USAGE_AND_PAGE Touchscreen = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_TOUCH_SCREEN}; + static const USAGE_AND_PAGE Touchpad = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_TOUCH_PAD}; ++ SrtEvdevCapabilities caps; + + struct udev_device *parent = dev; + +@@ -533,6 +831,33 @@ static const BYTE* what_am_I(struct udev_device *dev) + + parent = udev_device_get_parent_with_subsystem_devtype(parent, "input", NULL); + } ++ ++ /* In a container, udev properties might not be available. Fall back to deriving the device ++ * type from the fd's evdev capabilities. */ ++ if (_srt_evdev_capabilities_set_from_evdev (&caps, fd)) ++ { ++ SrtInputDeviceTypeFlags guessed_type; ++ ++ guessed_type = _srt_evdev_capabilities_guess_type (&caps); ++ ++ if (guessed_type & (SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE ++ | SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK)) ++ return &Mouse; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD) ++ return &Keyboard; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK) ++ return &Gamepad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS) ++ return &Keypad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD) ++ return &Touchpad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN) ++ return &Touchscreen; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET) ++ return &Tablet; ++ ++ /* Mapped to Unknown: ACCELEROMETER, TABLET_PAD, SWITCH. */ ++ } + + return &Unknown; + } +From 7926577b40f525e5c4081328e8c2f1dd113ef13b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 15 Dec 2020 12:23:31 -0600 +Subject: [PATCH] winebus.sys: Enable SDL input logging when hid channel is + enabled. + +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 84c8721270c..42bee61f0f0 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -112,6 +112,7 @@ MAKE_FUNCPTR(SDL_GameControllerAddMapping); + MAKE_FUNCPTR(SDL_RegisterEvents); + MAKE_FUNCPTR(SDL_PushEvent); + MAKE_FUNCPTR(SDL_GetTicks); ++MAKE_FUNCPTR(SDL_LogSetPriority); + static int (*pSDL_JoystickRumble)(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); + static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick); + static Uint16 (*pSDL_JoystickGetProductVersion)(SDL_Joystick * joystick); +@@ -1029,6 +1030,7 @@ NTSTATUS sdl_bus_init(void *args) + LOAD_FUNCPTR(SDL_RegisterEvents); + LOAD_FUNCPTR(SDL_PushEvent); + LOAD_FUNCPTR(SDL_GetTicks); ++ LOAD_FUNCPTR(SDL_LogSetPriority); + #undef LOAD_FUNCPTR + pSDL_JoystickRumble = dlsym(sdl_handle, "SDL_JoystickRumble"); + pSDL_JoystickGetProduct = dlsym(sdl_handle, "SDL_JoystickGetProduct"); +@@ -1047,6 +1049,11 @@ NTSTATUS sdl_bus_init(void *args) + goto failed; + } + ++ if (TRACE_ON(hid)) ++ { ++ pSDL_LogSetPriority(SDL_LOG_CATEGORY_INPUT, SDL_LOG_PRIORITY_VERBOSE); ++ } ++ + pSDL_JoystickEventState(SDL_ENABLE); + pSDL_GameControllerEventState(SDL_ENABLE); + +diff --git a/include/Makefile.in b/include/Makefile.in +index 4ea66b37f335..1a4f0cd9e6cd 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -840,6 +840,7 @@ SOURCES = \ + wine/irot.idl \ + wine/irpcss.idl \ + wine/itss.idl \ ++ wine/js_blacklist.h \ + wine/list.h \ + wine/mmsystem16.h \ + wine/mscvpdb.h \ diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/legacy/proton-sdl-joy-d1d1575.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/legacy/proton-sdl-joy-d1d1575.patch new file mode 100644 index 000000000..21886f6f5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/legacy/proton-sdl-joy-d1d1575.patch @@ -0,0 +1,921 @@ +From 10543c51d9fb259ccc061f49d09ec6e74603e25c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 6 Aug 2019 13:27:25 -0500 +Subject: [PATCH] winebus.sys: Disable UDEV lnxev devices by default. + +Based on a patch from Simon McVittie . +--- + dlls/winebus.sys/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c +index 111953c3bae..2a583871f1a 100644 +--- a/dlls/winebus.sys/main.c ++++ b/dlls/winebus.sys/main.c +@@ -742,7 +742,7 @@ static NTSTATUS udev_driver_init(void) + + bus_options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); + if (bus_options.disable_hidraw) TRACE("UDEV hidraw devices disabled in registry\n"); +- bus_options.disable_input = check_bus_option(L"DisableInput", 0); ++ bus_options.disable_input = check_bus_option(L"DisableInput", 1); + if (bus_options.disable_input) TRACE("UDEV input devices disabled in registry\n"); + bus_options.disable_udevd = check_bus_option(L"DisableUdevd", 0); + if (bus_options.disable_udevd) TRACE("UDEV udevd use disabled in registry\n"); +From 22d2f82678b9babe0a315df4f311221e999e7fbc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 30 Sep 2021 15:07:25 +0200 +Subject: [PATCH] HACK: winebus.sys: Prefer devices on UDEV hidraw bus over SDL + bus. + +--- + dlls/winebus.sys/main.c | 42 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 33 insertions(+), 9 deletions(-) + +diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c +index 2a583871f1a..1af7b5070f6 100644 +--- a/dlls/winebus.sys/main.c ++++ b/dlls/winebus.sys/main.c +@@ -71,6 +71,7 @@ struct device_extension + { + struct list entry; + DEVICE_OBJECT *device; ++ const WCHAR *bus_name; + + CRITICAL_SECTION cs; + enum device_state state; +@@ -284,7 +285,7 @@ static void remove_pending_irps(DEVICE_OBJECT *device) + } + } + +-static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, UINT64 unix_device) ++static DEVICE_OBJECT *bus_create_hid_device(const WCHAR *bus_name, struct device_desc *desc, UINT64 unix_device) + { + struct device_extension *ext; + DEVICE_OBJECT *device; +@@ -307,6 +308,7 @@ static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, struct uni + + /* fill out device_extension struct */ + ext = (struct device_extension *)device->DeviceExtension; ++ ext->bus_name = bus_name; + ext->device = device; + ext->desc = *desc; + ext->index = get_device_index(desc); +@@ -333,6 +335,17 @@ static DEVICE_OBJECT *bus_find_unix_device(struct unix_device *unix_device) + return NULL; + } + ++static DEVICE_OBJECT *bus_find_device_from_vid_pid(const WCHAR *bus_name, struct device_desc *desc) ++{ ++ struct device_extension *ext; ++ ++ LIST_FOR_EACH_ENTRY(ext, &device_list, struct device_extension, entry) ++ if (!wcscmp(ext->bus_name, bus_name) && ext->desc.vid == desc->vid && ++ ext->desc.pid == desc->pid) return ext->device; ++ ++ return NULL; ++} ++ + static void bus_unlink_hid_device(DEVICE_OBJECT *device) + { + struct device_extension *ext = (struct device_extension *)device->DeviceExtension; +@@ -519,7 +532,7 @@ static void mouse_device_create(void) + struct device_create_params params = {{0}}; + + if (winebus_call(mouse_create, ¶ms)) return; +- mouse_obj = bus_create_hid_device(¶ms.desc, params.device); ++ mouse_obj = bus_create_hid_device(L"WINEBUS", ¶ms.desc, params.device); + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + } + +@@ -528,7 +541,7 @@ static void keyboard_device_create(void) + struct device_create_params params = {{0}}; + + if (winebus_call(keyboard_create, ¶ms)) return; +- keyboard_obj = bus_create_hid_device(¶ms.desc, params.device); ++ keyboard_obj = bus_create_hid_device(L"WINEBUS", ¶ms.desc, params.device); + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + } + +@@ -574,7 +587,20 @@ static DWORD CALLBACK bus_main_thread(void *args) + IoInvalidateDeviceRelations(bus_pdo, BusRelations); + break; + case BUS_EVENT_TYPE_DEVICE_CREATED: +- device = bus_create_hid_device(&event->device_created.desc, event->device); ++ RtlEnterCriticalSection(&device_list_cs); ++ if (!wcscmp(bus.name, L"SDL")) ++ { ++ if (bus_find_device_from_vid_pid(L"UDEV", &event->device_created.desc)) device = NULL; ++ else device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ } ++ else if (!wcscmp(bus.name, L"UDEV")) ++ { ++ if ((device = bus_find_device_from_vid_pid(L"SDL", &event->device_created.desc))) ++ bus_unlink_hid_device(device); ++ device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ } ++ else device = bus_create_hid_device(bus.name, &event->device_created.desc, event->device); ++ RtlLeaveCriticalSection(&device_list_cs); + if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); + else + { +@@ -778,11 +804,9 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) + mouse_device_create(); + keyboard_device_create(); + +- if (!check_bus_option(L"Enable SDL", 1) || sdl_driver_init()) +- { +- udev_driver_init(); +- iohid_driver_init(); +- } ++ udev_driver_init(); ++ iohid_driver_init(); ++ sdl_driver_init(); + + irp->IoStatus.Status = STATUS_SUCCESS; + break; +From f33bc009edb3349d6b3302cbe9de994926a3a4c3 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 6 Aug 2019 13:37:38 -0500 +Subject: [PATCH] HACK: winebus.sys: Don't use hidraw for XBox controllers. + +--- + dlls/winebus.sys/bus_udev.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 2269f5b904e..334373f82e6 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1287,7 +1287,12 @@ static void udev_add_device(struct udev_device *dev, int fd) + } + + if (is_xbox_gamepad(desc.vid, desc.pid)) +- desc.is_gamepad = TRUE; ++ { ++ /* SDL handles xbox (and steam) controllers */ ++ TRACE("hidraw %s: ignoring %s, xbox gamepad\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + #ifdef HAS_PROPER_INPUT_HEADER + else + { +From 9d9d0ea6f5041b6f10a3ee031d2b0e72818841cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Thu, 30 Sep 2021 15:28:03 +0200 +Subject: [PATCH] HACK: winebus.sys: Don't use hidraw for Steam controllers. + +--- + dlls/winebus.sys/bus_udev.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 13 +++++++++++++ + 3 files changed, 21 insertions(+) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 334373f82e6..4d1124f5cc4 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1286,6 +1286,13 @@ static void udev_add_device(struct udev_device *dev, int fd) + memcpy(desc.serialnumber, zeros, sizeof(zeros)); + } + ++ if (is_steam_controller(desc.vid, desc.pid)) ++ { ++ /* this device is being used as a virtual Steam controller */ ++ TRACE("hidraw %s: ignoring %s, steam controller\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + if (is_xbox_gamepad(desc.vid, desc.pid)) + { + /* SDL handles xbox (and steam) controllers */ +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index efecf6cdbe3..6441f987a95 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; + + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; + ++BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; + +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index 1269ae05c2b..e88d2181446 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -38,6 +38,19 @@ + + #include "unix_private.h" + ++BOOL is_steam_controller(WORD vid, WORD pid) ++{ ++ if (vid != 0x28de) return FALSE; ++ if (pid == 0x1101) return TRUE; /* Valve Legacy Steam Controller */ ++ if (pid == 0x1102) return TRUE; /* Valve wired Steam Controller */ ++ if (pid == 0x1105) return TRUE; /* Valve Bluetooth Steam Controller */ ++ if (pid == 0x1106) return TRUE; /* Valve Bluetooth Steam Controller */ ++ if (pid == 0x1142) return TRUE; /* Valve wireless Steam Controller */ ++ if (pid == 0x1201) return TRUE; /* Valve wired Steam Controller */ ++ if (pid == 0x1202) return TRUE; /* Valve Bluetooth Steam Controller */ ++ return FALSE; ++} ++ + BOOL is_xbox_gamepad(WORD vid, WORD pid) + { + if (vid != 0x045e) return FALSE; +From 1cf8f159e76931d5e4c975e1a659bf8867fa8034 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 7 Oct 2021 15:29:05 +0200 +Subject: [PATCH] HACK: winebus.sys: Ignore blacklisted SDL controllers and + joysticks. + +--- + dlls/winebus.sys/bus_sdl.c | 27 +++++++++++++++++---------- + dlls/winebus.sys/bus_udev.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 18 ++++++++++++++++++ + 4 files changed, 43 insertions(+), 10 deletions(-) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 37d75d0bc95..7dfa8edb3b5 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -862,16 +862,8 @@ static void sdl_add_device(unsigned int index) + return; + } + +- if (options.map_controllers && pSDL_IsGameController(index)) +- controller = pSDL_GameControllerOpen(index); +- +- if (controller) product = pSDL_GameControllerName(controller); +- else product = pSDL_JoystickName(joystick); +- if (!product) product = "Joystick"; +- +- id = pSDL_JoystickInstanceID(joystick); +- +- if (pSDL_JoystickGetProductVersion != NULL) { ++ if (pSDL_JoystickGetProductVersion != NULL) ++ { + desc.vid = pSDL_JoystickGetVendor(joystick); + desc.pid = pSDL_JoystickGetProduct(joystick); + desc.version = pSDL_JoystickGetProductVersion(joystick); +@@ -883,6 +875,22 @@ static void sdl_add_device(unsigned int index) + desc.version = 0; + } + ++ if (is_sdl_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("ignoring %s, in SDL blacklist\n", debugstr_device_desc(&desc)); ++ return; ++ } ++ ++ if (options.map_controllers && pSDL_IsGameController(index)) ++ controller = pSDL_GameControllerOpen(index); ++ ++ if (controller) product = pSDL_GameControllerName(controller); ++ else product = pSDL_JoystickName(joystick); ++ if (!product) product = "Joystick"; ++ ++ id = pSDL_JoystickInstanceID(joystick); ++ + if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick))) + { + ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 4d1124f5cc4..3a525b235c6 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1286,6 +1286,13 @@ static void udev_add_device(struct udev_device *dev, int fd) + memcpy(desc.serialnumber, zeros, sizeof(zeros)); + } + ++ if (is_sdl_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("hidraw %s: ignoring %s, in SDL blacklist\n", debugstr_a(devnode), debugstr_device_desc(&desc)); ++ close(fd); ++ return; ++ } + if (is_steam_controller(desc.vid, desc.pid)) + { + /* this device is being used as a virtual Steam controller */ +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index 6441f987a95..02c9ff947c4 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; + + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; + ++BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; + BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index e88d2181446..8cff984ac20 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -38,6 +38,24 @@ + + #include "unix_private.h" + ++/* logic from SDL2's SDL_ShouldIgnoreGameController */ ++BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) ++{ ++ const char *allow_virtual = getenv("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD"); ++ const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"); ++ const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES"); ++ char needle[16]; ++ ++ if (vid == 0x28de && pid == 0x11ff && allow_virtual && *allow_virtual && ++ *allow_virtual != '0' && strcasecmp(allow_virtual, "false")) ++ return FALSE; ++ ++ sprintf(needle, "0x%04x/0x%04x", vid, pid); ++ if (whitelist) return strcasestr(whitelist, needle) == NULL; ++ if (blacklist) return strcasestr(blacklist, needle) != NULL; ++ return FALSE; ++} ++ + BOOL is_steam_controller(WORD vid, WORD pid) + { + if (vid != 0x28de) return FALSE; +From 7ea1cc2581b35b6d630d0399a0e230b3f57a2014 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 30 Aug 2019 10:20:16 -0500 +Subject: [PATCH] HACK: winebus.sys: Override Steam virtual controller vid/pid + with Xbox. + +Matches Windows Steam client behavior. +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 7dfa8edb3b5..079a615333e 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -882,6 +882,13 @@ static void sdl_add_device(unsigned int index) + return; + } + ++ if (desc.vid == 0x28de && desc.pid == 0x11ff) ++ { ++ TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); ++ desc.vid = 0x045e; ++ desc.pid = 0x028e; ++ } ++ + if (options.map_controllers && pSDL_IsGameController(index)) + controller = pSDL_GameControllerOpen(index); + +From e62a27bb284c4a7364d69fb09ae4df18c8860185 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 30 Sep 2021 20:38:04 +0200 +Subject: [PATCH] winebus.sys: Ignore some joysticks that SDL reports. + +SDL has a blacklist, but it isn't complete. Ignore some more devices +while we fix upstream. +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + dlls/winebus.sys/unix_private.h | 1 + + dlls/winebus.sys/unixlib.c | 14 ++++++++++++++ + include/wine/js_blacklist.h | 3 +++ + 4 files changed, 25 insertions(+) + create mode 100644 include/wine/js_blacklist.h + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 079a615333e..84c8721270c 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -882,6 +882,13 @@ static void sdl_add_device(unsigned int index) + return; + } + ++ if (is_wine_blacklisted(desc.vid, desc.pid)) ++ { ++ /* this device is blacklisted */ ++ TRACE("ignoring %s, in Wine blacklist\n", debugstr_device_desc(&desc)); ++ return; ++ } ++ + if (desc.vid == 0x28de && desc.pid == 0x11ff) + { + TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); +diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h +index 02c9ff947c4..a204eacb3bf 100644 +--- a/dlls/winebus.sys/unix_private.h ++++ b/dlls/winebus.sys/unix_private.h +@@ -265,6 +265,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; + extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; + + BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; ++BOOL is_wine_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; + BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; + BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; +diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +index 8cff984ac20..ae742b0cba4 100644 +--- a/dlls/winebus.sys/unixlib.c ++++ b/dlls/winebus.sys/unixlib.c +@@ -35,9 +35,23 @@ + #include "wine/debug.h" + #include "wine/list.h" + #include "wine/unixlib.h" ++#include "wine/js_blacklist.h" /* for wine_js_blacklist */ + + #include "unix_private.h" + ++BOOL is_wine_blacklisted(DWORD vid, DWORD pid) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(wine_js_blacklist); ++i) ++ { ++ if (vid != wine_js_blacklist[i].vid) continue; ++ if (!wine_js_blacklist[i].pid || wine_js_blacklist[i].pid == pid) return TRUE; ++ } ++ ++ return FALSE; ++} ++ + /* logic from SDL2's SDL_ShouldIgnoreGameController */ + BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) + { +diff --git a/include/wine/js_blacklist.h b/include/wine/js_blacklist.h +new file mode 100644 +index 00000000000..b8f2ec7dd28 +--- /dev/null ++++ b/include/wine/js_blacklist.h +@@ -0,0 +1,3 @@ ++static const struct { short vid; short pid; } wine_js_blacklist[] = { ++ {0x056a, 0x0000}, /* all Wacom devices */ ++}; +From 62f3031decd2500dbfc48c767cc662e812c1cf78 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 10 Nov 2020 18:32:28 +0000 +Subject: [PATCH] winebus.sys: Automatically bypass udevd in Flatpak or + pressure-vessel. + +Flatpak uses unprivileged containers that don't normally map uid 0 +into the container, so netlink events won't work there, as described +in previous commits. Steam's pressure-vessel container tool behaves +similarly. +--- + dlls/winebus.sys/bus_udev.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 3a525b235c6..5d8d2384e69 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -1708,6 +1708,12 @@ NTSTATUS udev_bus_init(void *args) + goto error; + } + ++ if (access("/run/pressure-vessel", R_OK) || access("/.flatpak-info", R_OK)) ++ { ++ TRACE("Container detected, bypassing udevd by default\n"); ++ options.disable_udevd = TRUE; ++ } ++ + #ifdef HAVE_SYS_INOTIFY_H + if (options.disable_udevd) monitor_fd = create_inotify(); + if (monitor_fd < 0) options.disable_udevd = FALSE; +From dd54394cee8090d2ef55c0da2ecde7a724c6a8c6 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 10 Nov 2020 19:03:47 +0000 +Subject: [PATCH] winebus.sys: Guess the type of evdev input devices. + +Ordinarily, we can get the type of an evdev input device from udev: +the input_id builtin sets udev properties of the form ID_INPUT_FOO +that we can read. + +However, in a container there is no guarantee that the libudev in the +container will interoperate with the udevd on the host system, so we +need to be prepared to do this ourselves from first principles, using +a heuristic similar to the one in udev's input_id. + +We cannot simply copy the heuristic from udev's input_id, because its +licensing is incompatible (GPL). Instead, use a vaguely similar heuristic +that works from the same inputs and will generally produce similar results. +--- + dlls/winebus.sys/bus_udev.c | 330 +++++++++++++++++++++++++++++++++++- + 1 file changed, 328 insertions(+), 2 deletions(-) + +diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c +index 5d8d2384e69..4592b095ff5 100644 +--- a/dlls/winebus.sys/bus_udev.c ++++ b/dlls/winebus.sys/bus_udev.c +@@ -25,6 +25,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -500,7 +501,303 @@ static struct base_device *find_device_from_syspath(const char *path) + + #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7))) + +-static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) ++/* Minimal compatibility with code taken from steam-runtime-tools */ ++typedef int gboolean; ++#define g_debug(fmt, ...) TRACE(fmt "\n", ## __VA_ARGS__) ++#define G_N_ELEMENTS(arr) (sizeof(arr)/sizeof(arr[0])) ++ ++typedef enum ++{ ++ SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK = (1 << 0), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER = (1 << 1), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD = (1 << 2), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS = (1 << 3), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE = (1 << 4), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD = (1 << 5), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN = (1 << 6), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET = (1 << 7), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET_PAD = (1 << 8), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK = (1 << 9), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_SWITCH = (1 << 10), ++ SRT_INPUT_DEVICE_TYPE_FLAGS_NONE = 0 ++} SrtInputDeviceTypeFlags; ++ ++#define BITS_PER_LONG (sizeof (unsigned long) * CHAR_BIT) ++#define LONGS_FOR_BITS(x) ((((x)-1)/BITS_PER_LONG)+1) ++typedef struct ++{ ++ unsigned long ev[LONGS_FOR_BITS (EV_MAX)]; ++ unsigned long keys[LONGS_FOR_BITS (KEY_MAX)]; ++ unsigned long abs[LONGS_FOR_BITS (ABS_MAX)]; ++ unsigned long rel[LONGS_FOR_BITS (REL_MAX)]; ++ unsigned long ff[LONGS_FOR_BITS (FF_MAX)]; ++ unsigned long props[LONGS_FOR_BITS (INPUT_PROP_MAX)]; ++} SrtEvdevCapabilities; ++ ++static gboolean ++_srt_get_caps_from_evdev (int fd, ++ unsigned int type, ++ unsigned long *bitmask, ++ size_t bitmask_len_longs) ++{ ++ size_t bitmask_len_bytes = bitmask_len_longs * sizeof (*bitmask); ++ ++ memset (bitmask, 0, bitmask_len_bytes); ++ ++ if (ioctl (fd, EVIOCGBIT (type, bitmask_len_bytes), bitmask) < 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static gboolean ++_srt_evdev_capabilities_set_from_evdev (SrtEvdevCapabilities *caps, ++ int fd) ++{ ++ if (_srt_get_caps_from_evdev (fd, 0, caps->ev, G_N_ELEMENTS (caps->ev))) ++ { ++ _srt_get_caps_from_evdev (fd, EV_KEY, caps->keys, G_N_ELEMENTS (caps->keys)); ++ _srt_get_caps_from_evdev (fd, EV_ABS, caps->abs, G_N_ELEMENTS (caps->abs)); ++ _srt_get_caps_from_evdev (fd, EV_REL, caps->rel, G_N_ELEMENTS (caps->rel)); ++ _srt_get_caps_from_evdev (fd, EV_FF, caps->ff, G_N_ELEMENTS (caps->ff)); ++ ioctl (fd, EVIOCGPROP (sizeof (caps->props)), caps->props); ++ return TRUE; ++ } ++ ++ memset (caps, 0, sizeof (*caps)); ++ return FALSE; ++} ++ ++#define JOYSTICK_ABS_AXES \ ++ ((1 << ABS_X) | (1 << ABS_Y) \ ++ | (1 << ABS_RX) | (1 << ABS_RY) \ ++ | (1 << ABS_THROTTLE) | (1 << ABS_RUDDER) \ ++ | (1 << ABS_WHEEL) | (1 << ABS_GAS) | (1 << ABS_BRAKE) \ ++ | (1 << ABS_HAT0X) | (1 << ABS_HAT0Y) \ ++ | (1 << ABS_HAT1X) | (1 << ABS_HAT1Y) \ ++ | (1 << ABS_HAT2X) | (1 << ABS_HAT2Y) \ ++ | (1 << ABS_HAT3X) | (1 << ABS_HAT3Y)) ++ ++static const unsigned int first_mouse_button = BTN_MOUSE; ++static const unsigned int last_mouse_button = BTN_JOYSTICK - 1; ++ ++static const unsigned int first_joystick_button = BTN_JOYSTICK; ++static const unsigned int last_joystick_button = BTN_GAMEPAD - 1; ++ ++static const unsigned int first_gamepad_button = BTN_GAMEPAD; ++static const unsigned int last_gamepad_button = BTN_DIGI - 1; ++ ++static const unsigned int first_dpad_button = BTN_DPAD_UP; ++static const unsigned int last_dpad_button = BTN_DPAD_RIGHT; ++ ++static const unsigned int first_extra_joystick_button = BTN_TRIGGER_HAPPY; ++static const unsigned int last_extra_joystick_button = BTN_TRIGGER_HAPPY40; ++ ++SrtInputDeviceTypeFlags ++_srt_evdev_capabilities_guess_type (const SrtEvdevCapabilities *caps) ++{ ++ SrtInputDeviceTypeFlags flags = SRT_INPUT_DEVICE_TYPE_FLAGS_NONE; ++ unsigned int i; ++ gboolean has_joystick_axes = FALSE; ++ gboolean has_joystick_buttons = FALSE; ++ ++ /* Some properties let us be fairly sure about a device */ ++ if (test_bit (caps->props, INPUT_PROP_ACCELEROMETER)) ++ { ++ g_debug ("INPUT_PROP_ACCELEROMETER => is accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ ++ if (test_bit (caps->props, INPUT_PROP_POINTING_STICK)) ++ { ++ g_debug ("INPUT_PROP_POINTING_STICK => is pointing stick"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK; ++ } ++ ++ if (test_bit (caps->props, INPUT_PROP_BUTTONPAD) ++ || test_bit (caps->props, INPUT_PROP_TOPBUTTONPAD)) ++ { ++ g_debug ("INPUT_PROP_[TOP]BUTTONPAD => is touchpad"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD; ++ } ++ ++ /* Devices with a stylus or pen are assumed to be graphics tablets */ ++ if (test_bit (caps->keys, BTN_STYLUS) ++ || test_bit (caps->keys, BTN_TOOL_PEN)) ++ { ++ g_debug ("Stylus or pen => is tablet"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET; ++ } ++ ++ /* Devices that accept a finger touch are assumed to be touchpads or ++ * touchscreens. ++ * ++ * In Steam we mostly only care about these as a way to ++ * reject non-joysticks, so we're not very precise here yet. ++ * ++ * SDL assumes that TOUCH means a touchscreen and FINGER ++ * means a touchpad. */ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE ++ && (test_bit (caps->keys, BTN_TOOL_FINGER) ++ || test_bit (caps->keys, BTN_TOUCH) ++ || test_bit (caps->props, INPUT_PROP_SEMI_MT))) ++ { ++ g_debug ("Finger or touch or semi-MT => is touchpad or touchscreen"); ++ ++ if (test_bit (caps->props, INPUT_PROP_POINTER)) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD; ++ else ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN; ++ } ++ ++ /* Devices with mouse buttons are ... probably mice? */ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE) ++ { ++ for (i = first_mouse_button; i <= last_mouse_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ { ++ g_debug ("Mouse button => mouse"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE; ++ } ++ } ++ } ++ ++ if (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE) ++ { ++ for (i = ABS_X; i < ABS_Z; i++) ++ { ++ if (!test_bit (caps->abs, i)) ++ break; ++ } ++ ++ /* If it has 3 axes and no buttons it's probably an accelerometer. */ ++ if (i == ABS_Z && !test_bit (caps->ev, EV_KEY)) ++ { ++ g_debug ("3 left axes and no buttons => accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ ++ /* Same for RX..RZ (e.g. Wiimote) */ ++ for (i = ABS_RX; i < ABS_RZ; i++) ++ { ++ if (!test_bit (caps->abs, i)) ++ break; ++ } ++ ++ if (i == ABS_RZ && !test_bit (caps->ev, EV_KEY)) ++ { ++ g_debug ("3 right axes and no buttons => accelerometer"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_ACCELEROMETER; ++ } ++ } ++ ++ /* Bits 1 to 31 are ESC, numbers and Q to D, which SDL and udev both ++ * consider to be enough to count as a fully-functioned keyboard. */ ++ if ((caps->keys[0] & 0xfffffffe) == 0xfffffffe) ++ { ++ g_debug ("First few keys => keyboard"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD; ++ } ++ ++ /* If we have *any* keys, consider it to be something a bit ++ * keyboard-like. Bits 0 to 63 are all keyboard keys. ++ * Make sure we stop before reaching KEY_UP which is sometimes ++ * used on game controller mappings, e.g. for the Wiimote. */ ++ for (i = 0; i < (64 / BITS_PER_LONG); i++) ++ { ++ if (caps->keys[i] != 0) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS; ++ } ++ ++ if (caps->abs[0] & JOYSTICK_ABS_AXES) ++ has_joystick_axes = TRUE; ++ ++ /* Flight stick buttons */ ++ for (i = first_joystick_button; i <= last_joystick_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Gamepad buttons (Xbox, PS3, etc.) */ ++ for (i = first_gamepad_button; i <= last_gamepad_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Gamepad digital dpad */ ++ for (i = first_dpad_button; i <= last_dpad_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Steering wheel gear-change buttons */ ++ for (i = BTN_GEAR_DOWN; i <= BTN_GEAR_UP; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ /* Reserved space for extra game-controller buttons, e.g. on Corsair ++ * gaming keyboards */ ++ for (i = first_extra_joystick_button; i <= last_extra_joystick_button; i++) ++ { ++ if (test_bit (caps->keys, i)) ++ has_joystick_buttons = TRUE; ++ } ++ ++ if (test_bit (caps->keys, last_mouse_button)) ++ { ++ /* Mice with a very large number of buttons can apparently ++ * overflow into the joystick-button space, but they're still not ++ * joysticks. */ ++ has_joystick_buttons = FALSE; ++ } ++ ++ /* TODO: Do we want to consider BTN_0 up to BTN_9 to be joystick buttons? ++ * libmanette and SDL look for BTN_1, udev does not. ++ * ++ * They're used by some game controllers, like BTN_1 and BTN_2 for the ++ * Wiimote, BTN_1..BTN_9 for the SpaceTec SpaceBall and BTN_0..BTN_3 ++ * for Playstation dance pads, but they're also used by ++ * non-game-controllers like Logitech mice. For now we entirely ignore ++ * these buttons: they are not evidence that it's a joystick, but ++ * neither are they evidence that it *isn't* a joystick. */ ++ ++ /* We consider it to be a joystick if there is some evidence that it is, ++ * and no evidence that it's something else. ++ * ++ * Unlike SDL, we accept devices with only axes and no buttons as a ++ * possible joystick, unless they have X/Y/Z axes in which case we ++ * assume they're accelerometers. */ ++ if ((has_joystick_buttons || has_joystick_axes) ++ && (flags == SRT_INPUT_DEVICE_TYPE_FLAGS_NONE)) ++ { ++ g_debug ("Looks like a joystick"); ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK; ++ } ++ ++ /* If we have *any* keys below BTN_MISC, consider it to be something ++ * a bit keyboard-like, but don't rule out *also* being considered ++ * to be a joystick (again for e.g. the Wiimote). */ ++ for (i = 0; i < (BTN_MISC / BITS_PER_LONG); i++) ++ { ++ if (caps->keys[i] != 0) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS; ++ } ++ ++ /* Also non-exclusive: don't rule out a device being a joystick and ++ * having a switch */ ++ if (test_bit (caps->ev, EV_SW)) ++ flags |= SRT_INPUT_DEVICE_TYPE_FLAGS_SWITCH; ++ ++ return flags; ++} ++ ++static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) + { + static const USAGE_AND_PAGE Unknown = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = 0}; + static const USAGE_AND_PAGE Mouse = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_MOUSE}; +@@ -510,6 +807,7 @@ static const BYTE* what_am_I(struct udev_device *dev) + static const USAGE_AND_PAGE Tablet = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_PEN}; + static const USAGE_AND_PAGE Touchscreen = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_TOUCH_SCREEN}; + static const USAGE_AND_PAGE Touchpad = {.UsagePage = HID_USAGE_PAGE_DIGITIZER, .Usage = HID_USAGE_DIGITIZER_TOUCH_PAD}; ++ SrtEvdevCapabilities caps; + + struct udev_device *parent = dev; + +@@ -533,6 +831,33 @@ static const BYTE* what_am_I(struct udev_device *dev) + + parent = udev_device_get_parent_with_subsystem_devtype(parent, "input", NULL); + } ++ ++ /* In a container, udev properties might not be available. Fall back to deriving the device ++ * type from the fd's evdev capabilities. */ ++ if (_srt_evdev_capabilities_set_from_evdev (&caps, fd)) ++ { ++ SrtInputDeviceTypeFlags guessed_type; ++ ++ guessed_type = _srt_evdev_capabilities_guess_type (&caps); ++ ++ if (guessed_type & (SRT_INPUT_DEVICE_TYPE_FLAGS_MOUSE ++ | SRT_INPUT_DEVICE_TYPE_FLAGS_POINTING_STICK)) ++ return &Mouse; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_KEYBOARD) ++ return &Keyboard; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_JOYSTICK) ++ return &Gamepad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_HAS_KEYS) ++ return &Keypad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHPAD) ++ return &Touchpad; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TOUCHSCREEN) ++ return &Touchscreen; ++ else if (guessed_type & SRT_INPUT_DEVICE_TYPE_FLAGS_TABLET) ++ return &Tablet; ++ ++ /* Mapped to Unknown: ACCELEROMETER, TABLET_PAD, SWITCH. */ ++ } + + return &Unknown; + } +From 7926577b40f525e5c4081328e8c2f1dd113ef13b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 15 Dec 2020 12:23:31 -0600 +Subject: [PATCH] winebus.sys: Enable SDL input logging when hid channel is + enabled. + +--- + dlls/winebus.sys/bus_sdl.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 84c8721270c..42bee61f0f0 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -112,6 +112,7 @@ MAKE_FUNCPTR(SDL_GameControllerAddMapping); + MAKE_FUNCPTR(SDL_RegisterEvents); + MAKE_FUNCPTR(SDL_PushEvent); + MAKE_FUNCPTR(SDL_GetTicks); ++MAKE_FUNCPTR(SDL_LogSetPriority); + static int (*pSDL_JoystickRumble)(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); + static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick); + static Uint16 (*pSDL_JoystickGetProductVersion)(SDL_Joystick * joystick); +@@ -1029,6 +1030,7 @@ NTSTATUS sdl_bus_init(void *args) + LOAD_FUNCPTR(SDL_RegisterEvents); + LOAD_FUNCPTR(SDL_PushEvent); + LOAD_FUNCPTR(SDL_GetTicks); ++ LOAD_FUNCPTR(SDL_LogSetPriority); + #undef LOAD_FUNCPTR + pSDL_JoystickRumble = dlsym(sdl_handle, "SDL_JoystickRumble"); + pSDL_JoystickGetProduct = dlsym(sdl_handle, "SDL_JoystickGetProduct"); +@@ -1047,6 +1049,11 @@ NTSTATUS sdl_bus_init(void *args) + goto failed; + } + ++ if (TRACE_ON(hid)) ++ { ++ pSDL_LogSetPriority(SDL_LOG_CATEGORY_INPUT, SDL_LOG_PRIORITY_VERBOSE); ++ } ++ + pSDL_JoystickEventState(SDL_ENABLE); + pSDL_GameControllerEventState(SDL_ENABLE); + +diff --git a/include/Makefile.in b/include/Makefile.in +index 4ea66b37f335..1a4f0cd9e6cd 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -840,6 +840,7 @@ SOURCES = \ + wine/irot.idl \ + wine/irpcss.idl \ + wine/itss.idl \ ++ wine/js_blacklist.h \ + wine/list.h \ + wine/mmsystem16.h \ + wine/mscvpdb.h \ diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy index 059329bdc..2a7b491cf 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy @@ -2,8 +2,12 @@ # SDL Joystick support - from Proton if [ "$_sdl_joy_support" = "true" ] && [ "$_use_staging" = "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor b57f635f22091674d9e249cb5e919bda45e8afb3 HEAD ); then # Proton 7.0 + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 13d8571b082102e030783bb13d6391b312c74aab HEAD ); then _patchname='proton-sdl-joy.patch' && _patchmsg="Enable SDL Joystick support (from Proton)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor d1d15757a44c59122b0592957597deaad06169c6 HEAD ); then + _patchname='proton-sdl-joy-13d8571.patch' && _patchmsg="Enable SDL Joystick support (from Proton)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor b57f635f22091674d9e249cb5e919bda45e8afb3 HEAD ); then # Proton 7.0 + _patchname='proton-sdl-joy-d1d1575.patch' && _patchmsg="Enable SDL Joystick support (from Proton)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 872dc83e81c942d17a39700c4b520a6e92ddbdf2 HEAD ); then # Proton 7.0 _patchname='proton-sdl-joy-b57f635.patch' && _patchmsg="Enable SDL Joystick support (from Proton)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 077d64bdbd0dcecf3f511976b4224750b7bc02dc HEAD ); then # Proton 7.0 diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy.patch index 21886f6f5..e63b19c26 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-sdl-joy/proton-sdl-joy.patch @@ -1,26 +1,3 @@ -From 10543c51d9fb259ccc061f49d09ec6e74603e25c Mon Sep 17 00:00:00 2001 -From: Andrew Eikum -Date: Tue, 6 Aug 2019 13:27:25 -0500 -Subject: [PATCH] winebus.sys: Disable UDEV lnxev devices by default. - -Based on a patch from Simon McVittie . ---- - dlls/winebus.sys/main.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c -index 111953c3bae..2a583871f1a 100644 ---- a/dlls/winebus.sys/main.c -+++ b/dlls/winebus.sys/main.c -@@ -742,7 +742,7 @@ static NTSTATUS udev_driver_init(void) - - bus_options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); - if (bus_options.disable_hidraw) TRACE("UDEV hidraw devices disabled in registry\n"); -- bus_options.disable_input = check_bus_option(L"DisableInput", 0); -+ bus_options.disable_input = check_bus_option(L"DisableInput", 1); - if (bus_options.disable_input) TRACE("UDEV input devices disabled in registry\n"); - bus_options.disable_udevd = check_bus_option(L"DisableUdevd", 0); - if (bus_options.disable_udevd) TRACE("UDEV udevd use disabled in registry\n"); From 22d2f82678b9babe0a315df4f311221e999e7fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Sep 2021 15:07:25 +0200 @@ -97,9 +74,9 @@ index 2a583871f1a..1af7b5070f6 100644 } @@ -574,7 +587,20 @@ static DWORD CALLBACK bus_main_thread(void *args) - IoInvalidateDeviceRelations(bus_pdo, BusRelations); - break; - case BUS_EVENT_TYPE_DEVICE_CREATED: + break; + } + - device = bus_create_hid_device(&event->device_created.desc, event->device); + RtlEnterCriticalSection(&device_list_cs); + if (!wcscmp(bus.name, L"SDL")) @@ -118,21 +95,6 @@ index 2a583871f1a..1af7b5070f6 100644 if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); else { -@@ -778,11 +804,9 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) - mouse_device_create(); - keyboard_device_create(); - -- if (!check_bus_option(L"Enable SDL", 1) || sdl_driver_init()) -- { -- udev_driver_init(); -- iohid_driver_init(); -- } -+ udev_driver_init(); -+ iohid_driver_init(); -+ sdl_driver_init(); - - irp->IoStatus.Status = STATUS_SUCCESS; - break; From f33bc009edb3349d6b3302cbe9de994926a3a4c3 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Tue, 6 Aug 2019 13:37:38 -0500 @@ -167,8 +129,7 @@ Subject: [PATCH] HACK: winebus.sys: Don't use hidraw for Steam controllers. --- dlls/winebus.sys/bus_udev.c | 7 +++++++ - dlls/winebus.sys/unix_private.h | 1 + - dlls/winebus.sys/unixlib.c | 13 +++++++++++++ + dlls/winebus.sys/unixlib.h | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c @@ -189,27 +150,15 @@ index 334373f82e6..4d1124f5cc4 100644 if (is_xbox_gamepad(desc.vid, desc.pid)) { /* SDL handles xbox (and steam) controllers */ -diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h -index efecf6cdbe3..6441f987a95 100644 ---- a/dlls/winebus.sys/unix_private.h -+++ b/dlls/winebus.sys/unix_private.h -@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; - - extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; - -+BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; - -diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 1269ae05c2b..e88d2181446 100644 ---- a/dlls/winebus.sys/unixlib.c -+++ b/dlls/winebus.sys/unixlib.c -@@ -38,6 +38,19 @@ - - #include "unix_private.h" +--- a/dlls/winebus.sys/unixlib.h ++++ b/dlls/winebus.sys/unixlib.h +@@ -151,6 +151,19 @@ + desc->vid, desc->pid, desc->version, desc->input, desc->uid, desc->is_gamepad, desc->is_hidraw); + } -+BOOL is_steam_controller(WORD vid, WORD pid) ++static inline BOOL is_steam_controller(WORD vid, WORD pid) +{ + if (vid != 0x28de) return FALSE; + if (pid == 0x1101) return TRUE; /* Valve Legacy Steam Controller */ @@ -222,7 +171,7 @@ index 1269ae05c2b..e88d2181446 100644 + return FALSE; +} + - BOOL is_xbox_gamepad(WORD vid, WORD pid) + static inline BOOL is_xbox_gamepad(WORD vid, WORD pid) { if (vid != 0x045e) return FALSE; From 1cf8f159e76931d5e4c975e1a659bf8867fa8034 Mon Sep 17 00:00:00 2001 @@ -234,8 +183,7 @@ Subject: [PATCH] HACK: winebus.sys: Ignore blacklisted SDL controllers and --- dlls/winebus.sys/bus_sdl.c | 27 +++++++++++++++++---------- dlls/winebus.sys/bus_udev.c | 7 +++++++ - dlls/winebus.sys/unix_private.h | 1 + - dlls/winebus.sys/unixlib.c | 18 ++++++++++++++++++ + dlls/winebus.sys/unixlib.h | 18 ++++++++++++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c @@ -302,28 +250,16 @@ index 4d1124f5cc4..3a525b235c6 100644 if (is_steam_controller(desc.vid, desc.pid)) { /* this device is being used as a virtual Steam controller */ -diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h -index 6441f987a95..02c9ff947c4 100644 ---- a/dlls/winebus.sys/unix_private.h -+++ b/dlls/winebus.sys/unix_private.h -@@ -264,6 +264,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; - - extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; - -+BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; - BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; -diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c -index e88d2181446..8cff984ac20 100644 ---- a/dlls/winebus.sys/unixlib.c -+++ b/dlls/winebus.sys/unixlib.c -@@ -38,6 +38,24 @@ - - #include "unix_private.h" +diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h +index 1269ae05c2b..e88d2181446 100644 +--- a/dlls/winebus.sys/unixlib.h ++++ b/dlls/winebus.sys/unixlib.h +@@ -151,6 +151,24 @@ + desc->vid, desc->pid, desc->version, desc->input, desc->uid, desc->is_gamepad, desc->is_hidraw); + } +/* logic from SDL2's SDL_ShouldIgnoreGameController */ -+BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) ++static inline BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) +{ + const char *allow_virtual = getenv("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD"); + const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"); @@ -340,9 +276,10 @@ index e88d2181446..8cff984ac20 100644 + return FALSE; +} + - BOOL is_steam_controller(WORD vid, WORD pid) + static inline BOOL is_steam_controller(WORD vid, WORD pid) { if (vid != 0x28de) return FALSE; + From 7ea1cc2581b35b6d630d0399a0e230b3f57a2014 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Fri, 30 Aug 2019 10:20:16 -0500 @@ -405,31 +342,23 @@ index 079a615333e..84c8721270c 100644 if (desc.vid == 0x28de && desc.pid == 0x11ff) { TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n"); -diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h -index 02c9ff947c4..a204eacb3bf 100644 ---- a/dlls/winebus.sys/unix_private.h -+++ b/dlls/winebus.sys/unix_private.h -@@ -265,6 +265,7 @@ extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN; - extern void hid_device_set_effect_state(struct unix_device *iface, BYTE index, BYTE flags) DECLSPEC_HIDDEN; - - BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; -+BOOL is_wine_blacklisted(DWORD vid, DWORD pid) DECLSPEC_HIDDEN; - BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; - BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; -diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c +diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 8cff984ac20..ae742b0cba4 100644 ---- a/dlls/winebus.sys/unixlib.c -+++ b/dlls/winebus.sys/unixlib.c -@@ -35,9 +35,23 @@ +--- a/dlls/winebus.sys/unixlib.h ++++ b/dlls/winebus.sys/unixlib.h +@@ -28,6 +28,7 @@ + #include "wine/debug.h" #include "wine/list.h" - #include "wine/unixlib.h" +#include "wine/js_blacklist.h" /* for wine_js_blacklist */ - #include "unix_private.h" + #include "wine/debug.h" + #include "wine/list.h" +@@ -151,6 +151,19 @@ + desc->vid, desc->pid, desc->version, desc->input, desc->uid, desc->is_gamepad, desc->is_hidraw); + } -+BOOL is_wine_blacklisted(DWORD vid, DWORD pid) ++static inline BOOL is_wine_blacklisted(DWORD vid, DWORD pid) +{ + int i; + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/legacy/proton-steam-bits-7ada0e2.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/legacy/proton-steam-bits-7ada0e2.patch new file mode 100644 index 000000000..55c95bfc9 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/legacy/proton-steam-bits-7ada0e2.patch @@ -0,0 +1,737 @@ +From 60365fa2e1f55e51b44788d5eac68c6ddce77266 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Dec 2017 13:31:59 -0600 +Subject: [PATCH] HACK: steam: wine.inf: Add required Steam registry entries. + +--- + loader/wine.inf.in | 25 ++++++++++++++++++++++--- + 1 file changed, 22 insertions(+), 3 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 5f9f61e2535..43cae2a2bc2 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -96,7 +97,8 @@ AddReg=\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ +- LicenseInformation ++ LicenseInformation, \ ++ SteamClient + + [DefaultInstall.ntamd64] + RegisterDlls=RegisterDllsSection +@@ -122,7 +124,8 @@ AddReg=\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ +- LicenseInformation ++ LicenseInformation, \ ++ SteamClient.ntamd64 + + [DefaultInstall.ntarm64] + RegisterDlls=RegisterDllsSection +@@ -148,7 +150,8 @@ AddReg=\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ +- LicenseInformation ++ LicenseInformation, \ ++ SteamClient.ntamd64 + + [Wow64Install] + RegisterDlls=RegisterDllsSection +@@ -164,7 +166,8 @@ AddReg=\ + Misc,\ + Tapi,\ + VersionInfo,\ +- LicenseInformation ++ LicenseInformation, \ ++ SteamClient + + + [DefaultInstall.Services] +@@ -3867,6 +3870,22 @@ l_intl.nls + winehid.inf,"@%12%\winehid.sys,-1" + wineusb.inf,"@%12%\wineusb.sys,-1" + winexinput.inf,"@%12%\winexinput.sys,-1" ++ ++[SteamClient] ++HKCU,Software\Valve\Steam,"SteamPath",,"%16422%\Steam" ++HKCU,Software\Valve\Steam,"SteamExe",,"%16422%\Steam\Steam.exe" ++HKCU,Software\Valve\Steam\ActiveProcess,"PID",0x10001,0x0000fffe ++HKCU,Software\Valve\Steam\ActiveProcess,"SteamClientDll",,"%16422%\Steam\steamclient.dll" ++HKCU,Software\Valve\Steam\ActiveProcess,"SteamPath",,"%16422%\Steam" ++ ++[SteamClient.ntamd64] ++HKCU,Software\Valve\Steam,"SteamPath",,"%16422%\Steam" ++HKCU,Software\Valve\Steam,"SteamExe",,"%16422%\Steam\Steam.exe" ++HKCU,Software\Valve\Steam\ActiveProcess,"PID",0x10001,0x0000fffe ++HKCU,Software\Valve\Steam\ActiveProcess,"SteamClientDll",,"%16426%\Steam\steamclient.dll" ++HKCU,Software\Valve\Steam\ActiveProcess,"SteamClientDll64",,"%16426%\Steam\steamclient64.dll" ++HKCU,Software\Valve\Steam\ActiveProcess,"SteamPath",,"%16426%\Steam" ++HKLM,Software\Wow6432Node\Valve\Steam,"InstallPath",,"%16422%\Steam" + + [NlsFiles] + c_037.nls + +From f725f3ce82ddb6b8ab9d427dda5dcd30b512b7f8 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 27 Apr 2017 13:25:04 -0500 +Subject: [PATCH] HACK: steam: kernelbase: Substitute the current pid for the + Steam client pid. + +--- + dlls/kernelbase/process.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 12187b92e5c..a22b59ad80b 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -1014,6 +1014,21 @@ HANDLE WINAPI DECLSPEC_HOTPATCH OpenProcess( DWORD access, BOOL inherit, DWORD i + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + ++ /* PROTON HACK: ++ * On Windows, the Steam client puts its process ID into the registry ++ * at: ++ * ++ * [HKCU\Software\Valve\Steam\ActiveProcess] ++ * PID=dword:00000008 ++ * ++ * Games get that pid from the registry and then query it with ++ * OpenProcess to ensure Steam is running. Since we aren't running the ++ * Windows Steam in Wine, instead we hack this magic number into the ++ * registry and then substitute the game's process itself in its place ++ * so it can query a valid process. ++ */ ++ if (id == 0xfffe) id = GetCurrentProcessId(); ++ + cid.UniqueProcess = ULongToHandle(id); + cid.UniqueThread = 0; + +From df134c39cab2e1a32c75d34d7b9348482abe73d1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 19 Jan 2018 14:01:07 -0600 +Subject: [PATCH] HACK: proton: advapi32: Use steamuser as Wine username. + +--- + dlls/advapi32/advapi.c | 34 ++++++++++++++++++---------------- + 1 file changed, 18 insertions(+), 16 deletions(-) + +diff --git a/dlls/advapi32/advapi.c b/dlls/advapi32/advapi.c +index 589405b59e6..a31a05f0250 100644 +--- a/dlls/advapi32/advapi.c ++++ b/dlls/advapi32/advapi.c +@@ -44,14 +44,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(advapi); + */ + BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + { +- DWORD len = GetEnvironmentVariableA( "WINEUSERNAME", name, *size ); +- BOOL ret; +- +- if (!len) return FALSE; +- if ((ret = (len < *size))) len++; +- else SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = len; +- return ret; ++ static const char steamuserA[] = {'s','t','e','a','m','u','s','e','r',0}; ++ if(*size < ARRAY_SIZE(steamuserA)){ ++ SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = ARRAY_SIZE(steamuserA); ++ return FALSE; ++ } ++ memcpy(name, steamuserA, sizeof(steamuserA)); ++ *size = ARRAY_SIZE(steamuserA); ++ return TRUE; + } + + /****************************************************************************** +@@ -59,14 +60,15 @@ BOOL WINAPI GetUserNameA( LPSTR name, LPDWORD size ) + */ + BOOL WINAPI GetUserNameW( LPWSTR name, LPDWORD size ) + { +- DWORD len = GetEnvironmentVariableW( L"WINEUSERNAME", name, *size ); +- BOOL ret; +- +- if (!len) return FALSE; +- if ((ret = (len < *size))) len++; +- else SetLastError( ERROR_INSUFFICIENT_BUFFER ); +- *size = len; +- return ret; ++ static const WCHAR steamuserW[] = {'s','t','e','a','m','u','s','e','r',0}; ++ if(*size < ARRAY_SIZE(steamuserW)){ ++ SetLastError( ERROR_INSUFFICIENT_BUFFER ); ++ *size = ARRAY_SIZE(steamuserW); ++ return FALSE; ++ } ++ memcpy(name, steamuserW, sizeof(steamuserW)); ++ *size = ARRAY_SIZE(steamuserW); ++ return TRUE; + } + + /****************************************************************************** +From ec9e7190ea7045012b6e33e5ee64cd678bec68c9 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 22 Jan 2018 14:32:40 -0600 +Subject: [PATCH] HACK: shell32: Never create links to the user's home dirs + +--- + dlls/shell32/shellpath.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c +index f08474b3635..2f637f25537 100644 +--- a/dlls/shell32/shellpath.c ++++ b/dlls/shell32/shellpath.c +@@ -4369,7 +4369,7 @@ static void _SHCreateMyDocumentsSymbolicLink(const UINT * aidsMyStuff, const UIN + + /* create symbolic links rather than directories for specific + * user shell folders */ +- _SHCreateSymbolicLink(folder, szBuildPath); ++ // _SHCreateSymbolicLink(folder, szBuildPath); + + /* create directory/directories */ + ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL); +From 63f934962cb97d69ed71604985716e7479f822b1 Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Tue, 23 Oct 2018 16:18:20 +0300 +Subject: [PATCH] wine.inf: Add font registry entries. + +--- + loader/wine.inf.in | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 1f94c38d0c6..df2b43d9106 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -181,6 +181,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" + Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" ++FontsNT="Software\Microsoft\Windows NT\CurrentVersion\Fonts" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -614,6 +615,10 @@ 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" + HKLM,System\CurrentControlSet\Hardware Profiles\Current\Software\Fonts,"LogPixels",0x10003,0x00000060 ++HKLM,%FontsNT%,"Arial (TrueType)",,"arial.ttf" ++HKLM,%FontsNT%,"Arial Bold (TrueType)",,"arialbd.ttf" ++HKLM,%FontsNT%,"Times New Roman (TrueType)",,"times.ttf" ++HKLM,%FontsNT%,"Courier New (TrueType)",,"cour.ttf" + + [MCI] + HKLM,%Mci32Str%,"AVIVideo",,"mciavi32.dll" + +From 174d487bf8ef5bec22c3663f50a276ae59d0a931 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 30 Oct 2018 13:04:06 -0500 +Subject: [PATCH] wine.inf: Substitute Times New Roman for Palatino Linotype + +For AOE2HD launcher. +--- + loader/wine.inf.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index df2b43d9106..44ce3cd6639 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -619,6 +619,7 @@ HKLM,%FontsNT%,"Arial (TrueType)",,"arial.ttf" + HKLM,%FontsNT%,"Arial Bold (TrueType)",,"arialbd.ttf" + HKLM,%FontsNT%,"Times New Roman (TrueType)",,"times.ttf" + HKLM,%FontsNT%,"Courier New (TrueType)",,"cour.ttf" ++HKCU,Software\Wine\Fonts\Replacements,"Palatino Linotype",,"Times New Roman" + + [MCI] + HKLM,%Mci32Str%,"AVIVideo",,"mciavi32.dll" + +From 12b7edaf69d1f9c94c18bd4cd8ae0e1d5ae97972 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 15 Jan 2019 10:10:47 -0600 +Subject: [PATCH] ntdll: Don't pass SDL_AUDIODRIVER from Linux environment + +--- + dlls/ntdll/unix/env.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index 74cea11be5f..50bc2321eaa 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -527,6 +527,7 @@ static BOOL is_special_env_var( const char *var ) + STARTS_WITH( var, "TEMP=" ) || + STARTS_WITH( var, "TMP=" ) || + STARTS_WITH( var, "QT_" ) || ++ STARTS_WITH( var, "SDL_AUDIODRIVER=" ) || + STARTS_WITH( var, "VK_" )); + } + +From 17d19e1b23bce6fc6ff5b4e61131c9c58fd57ae5 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 28 Mar 2018 09:21:41 -0500 +Subject: [PATCH] HACK: Don't build winemenubuilder + +--- + configure | 2 -- + configure.ac | 1 - + loader/wine.inf.in | 1 - + 3 files changed, 4 deletions(-) + +diff --git a/configure b/configure +index 649a8917f54..4581015d576 100755 +--- a/configure ++++ b/configure +@@ -1746,7 +1746,6 @@ enable_wineconsole + enable_winedbg + enable_winedevice + enable_winefile +-enable_winemenubuilder + enable_winemine + enable_winemsibuilder + enable_winepath +@@ -20315,7 +20314,6 @@ wine_fn_config_makefile programs/wineconsole enable_wineconsole + wine_fn_config_makefile programs/winedbg enable_winedbg + wine_fn_config_makefile programs/winedevice enable_winedevice + wine_fn_config_makefile programs/winefile enable_winefile +-wine_fn_config_makefile programs/winemenubuilder enable_winemenubuilder + wine_fn_config_makefile programs/winemine enable_winemine + wine_fn_config_makefile programs/winemsibuilder enable_winemsibuilder + wine_fn_config_makefile programs/winepath enable_winepath +diff --git a/configure.ac b/configure.ac +index 704da15f404..d4168e410bc 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3998,7 +3998,6 @@ WINE_CONFIG_MAKEFILE(programs/wineconsole) + WINE_CONFIG_MAKEFILE(programs/winedbg) + WINE_CONFIG_MAKEFILE(programs/winedevice) + WINE_CONFIG_MAKEFILE(programs/winefile) +-WINE_CONFIG_MAKEFILE(programs/winemenubuilder) + WINE_CONFIG_MAKEFILE(programs/winemine) + WINE_CONFIG_MAKEFILE(programs/winemsibuilder) + WINE_CONFIG_MAKEFILE(programs/winepath) +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 2b049eb91f8..e6948a73427 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -3598,7 +3598,6 @@ HKR,Parameters,"ServiceDll",,"%11%\schedsvc.dll" + HKLM,%CurrentVersionNT%\SvcHost,"netsvcs",0x00010008,"Schedule" + + [Services] +-HKLM,%CurrentVersion%\RunServices,"winemenubuilder",2,"%11%\winemenubuilder.exe -a -r" + HKLM,"System\CurrentControlSet\Services\Eventlog\Application",,16 + HKLM,"System\CurrentControlSet\Services\Eventlog\System","Sources",0x10000,"" + HKLM,"System\CurrentControlSet\Services\Tcpip\Parameters","DataBasePath",2,"%11%\drivers" + +From ea8d4cc0a661d5588383baa2ed1a6c56d2e66ffc Mon Sep 17 00:00:00 2001 +From: Patryk Obara +Date: Fri, 26 Apr 2019 20:40:31 +0200 +Subject: [PATCH] HACK: proton: winex11: Fill WM_CLASS based on Steam appid. + +Some desktop environments (Gnome 3, Cinnamon) decide on an application +icon in the following order: + +- If the first string in WM_CLASS property can be correlated to + a name or StartupWMClass key in a .desktop entry file, then + the associated icon will be used. +- If the second string in WM_CLASS property can be correlated to + a name or StartupWMClass key in a .desktop entry file, then + the associated icon will be used. +- If the application has indicated an icon resource through WM_HINTS + property, then the associated X window or pixmaps will be used. + +Upstream Wine usually deals with this by placing a .desktop file with +StartupWMClass filled to match first string in WM_CLASS property +(which is the name of exe file being run). + +Wine in Proton does not do it, but still puts "Wine" as second string, +therefore desktop environment can't differentiate between Wine in +Proton and Wine installed in OS. + +By replacing "Wine" with "steam_app_" we force DE to fallback +to icon indicated by WM_HINTS (ico file embedded in exe file). +Steam can override this behaviour by installing properly crafted +.desktop entry file. If SteamAppId environment variable is missing, +then generic "steam_proton" name is used instead. +--- + dlls/winex11.drv/window.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 36fb41ac710..916350c685a 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -855,8 +855,19 @@ static void set_initial_wm_hints( Display *display, Window window ) + /* class hints */ + if ((class_hints = XAllocClassHint())) + { +- class_hints->res_name = process_name; +- class_hints->res_class = process_name; ++ static char steam_proton[] = "steam_proton"; ++ const char *app_id = getenv("SteamAppId"); ++ char proton_app_class[128]; ++ ++ if(app_id && *app_id){ ++ snprintf(proton_app_class, sizeof(proton_app_class), "steam_app_%s", app_id); ++ class_hints->res_name = proton_app_class; ++ class_hints->res_class = proton_app_class; ++ }else{ ++ class_hints->res_name = steam_proton; ++ class_hints->res_class = steam_proton; ++ } ++ + XSetClassHint( display, window, class_hints ); + XFree( class_hints ); + } +From 30d2ea38ead1a2f1de872c9725e67084feaf5024 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 24 Sep 2018 12:37:49 -0500 +Subject: [PATCH] HACK: proton: HACK: dbghelp: Disable DWARF parsing + +Patch by Zeb. Our DWARF parser has been known to crash winedbg in some +cases. Since probably no concerned parties are going to be using plain +winedbg, just don't bother parsing anything. +--- + dlls/dbghelp/dwarf.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c +index 7472b6070e6..e049ee51525 100644 +--- a/dlls/dbghelp/dwarf.c ++++ b/dlls/dbghelp/dwarf.c +@@ -4162,6 +4162,11 @@ BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, + struct module_format* dwarf2_modfmt; + dwarf2_parse_module_context_t module_ctx; + ++/* Our DWARF parser has been known to crash winedbg in some cases. Since ++ * probably no concerned parties are going to be using plain winedbg, just don't ++ * bother parsing anything. */ ++return FALSE; ++ + if (!dwarf2_init_section(&eh_frame, fmap, ".eh_frame", NULL, &eh_frame_sect)) + /* lld produces .eh_fram to avoid generating a long name */ + dwarf2_init_section(&eh_frame, fmap, ".eh_fram", NULL, &eh_frame_sect); + +From daabcc156e6b63cf438aa004ac3f40a741872ad4 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 30 Mar 2018 10:40:43 -0500 +Subject: [PATCH] HACK: proton: winedbg: When crash dialog is not shown, dump + crash info to stderr + +This way the backtrace and such will appear in the log file instead of +going to stdout, which we don't capture. +--- + programs/winedbg/tgt_active.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c +index f632ca0414d..72e63327831 100644 +--- a/programs/winedbg/tgt_active.c ++++ b/programs/winedbg/tgt_active.c +@@ -965,6 +965,9 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) + if (event) thread = display_crash_details( event ); + if (thread) dbg_houtput = output = create_temp_file(); + break; ++ case TRUE: ++ dbg_houtput = GetStdHandle(STD_ERROR_HANDLE); ++ break; + } + + input = parser_generate_command_file("echo Modules:", "info share", +From 05b798206ccddbf1a546a97746d32aff2ca1d8b9 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 May 2021 13:34:47 -0500 +Subject: [PATCH] HACK: proton: winedbg: Support dumping crash logs to a + directory + +CW-Bug-Id: #18944 +--- + programs/winedbg/debugger.h | 1 + + programs/winedbg/tgt_active.c | 45 +++++++++++++++++++++++++++++++++++ + programs/winedbg/winedbg.c | 3 +++ + 3 files changed, 49 insertions(+) + +diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h +index b676bc8d955..e4e40bb487f 100644 +--- a/programs/winedbg/debugger.h ++++ b/programs/winedbg/debugger.h +@@ -283,6 +283,7 @@ extern DWORD dbg_curr_tid; + extern dbg_ctx_t dbg_context; + extern BOOL dbg_interactiveP; + extern HANDLE dbg_houtput; ++extern HANDLE dbg_crash_report_file; + + struct dbg_internal_var + { +diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c +index 5a26e1aef1f..d89a76272b0 100644 +--- a/programs/winedbg/tgt_active.c ++++ b/programs/winedbg/tgt_active.c +@@ -22,6 +22,8 @@ + #include + #include + #include ++#include ++#include + + #include "debugger.h" + #include "psapi.h" +@@ -784,6 +786,48 @@ static HANDLE create_temp_file(void) + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0 ); + } + ++static HANDLE create_crash_report_file(void) ++{ ++ const char *dir = getenv("WINE_CRASH_REPORT_DIR"); ++ const char *sgi; ++ char timestr[32]; ++ char name[MAX_PATH], *c; ++ time_t t; ++ struct tm lt; ++ ++ if(!dir || dir[0] == 0) ++ return INVALID_HANDLE_VALUE; ++ ++ strcpy(name, dir); ++ ++ for(c = name + 1; *c; ++c){ ++ if(*c == '/'){ ++ *c = 0; ++ CreateDirectoryA(name, NULL); ++ *c = '/'; ++ } ++ } ++ CreateDirectoryA(name, NULL); ++ ++ sgi = getenv("SteamGameId"); ++ ++ t = time(NULL); ++ lt = *localtime(&t); ++ strftime(timestr, ARRAY_SIZE(timestr), "%Y-%m-%d_%H:%M:%S", <); ++ ++ /* /path/to/crash/reports/2021-05-18_13:21:15_appid-976310_crash.log */ ++ snprintf(name, ARRAY_SIZE(name), ++ "%s%s/%s_appid-%s_crash.log", ++ dir[0] == '/' ? "Z:/" : "", ++ dir, ++ timestr, ++ sgi ? sgi : "0" ++ ); ++ ++ return CreateFileA( name, GENERIC_WRITE, FILE_SHARE_READ, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); ++} ++ + static const struct + { + int type; +@@ -967,6 +1011,7 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) + break; + case TRUE: + dbg_houtput = GetStdHandle(STD_ERROR_HANDLE); ++ dbg_crash_report_file = create_crash_report_file(); + break; + } + +diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c +index dab5fbd85a3..21391bcdcec 100644 +--- a/programs/winedbg/winedbg.c ++++ b/programs/winedbg/winedbg.c +@@ -82,6 +82,7 @@ DWORD dbg_curr_pid = 0; + dbg_ctx_t dbg_context; + BOOL dbg_interactiveP = FALSE; + HANDLE dbg_houtput = 0; ++HANDLE dbg_crash_report_file = INVALID_HANDLE_VALUE; + + static struct list dbg_process_list = LIST_INIT(dbg_process_list); + +@@ -108,6 +109,8 @@ static void dbg_outputA(const char* buffer, int len) + else break; + } + WriteFile(dbg_houtput, line_buff, i, &w, NULL); ++ if (dbg_crash_report_file != INVALID_HANDLE_VALUE) ++ WriteFile(dbg_crash_report_file, line_buff, i, &w, NULL); + memmove( line_buff, line_buff + i, line_pos - i ); + line_pos -= i; + } + +From a7542e9e279970c2ab3ac9c6c986b300135286a0 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 28 Mar 2018 09:17:30 -0500 +Subject: [PATCH] wine.inf: Don't show crash dialog by default + +--- + loader/wine.inf.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 7fe2f49dcd9..e72d0dcaba1 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -540,6 +540,7 @@ HKLM,%CurrentVersionNT%\AeDebug,"Debugger",2,"winedbg --auto %ld %ld" + HKLM,%CurrentVersionNT%\AeDebug,"Auto",2,"1" + HKCU,Software\Wine\Debug,"RelayExclude",2,"ntdll.RtlEnterCriticalSection;ntdll.RtlTryEnterCriticalSection;ntdll.RtlLeaveCriticalSection;kernel32.48;kernel32.49;kernel32.94;kernel32.95;kernel32.96;kernel32.97;kernel32.98;kernel32.TlsGetValue;kernel32.TlsSetValue;kernel32.FlsGetValue;kernel32.FlsSetValue;kernel32.SetLastError" + HKCU,Software\Wine\Debug,"RelayFromExclude",2,"winex11.drv;winemac.drv;user32;gdi32;advapi32;kernel32" ++HKCU,Software\Wine\WineDbg,"ShowCrashDialog",0x00010003,0x00000000 + + [DirectX] + HKLM,Software\Microsoft\DirectX,"Version",,"4.09.00.0904" + +From ec87f47caa8a9461c2a983cb4cf7d0b7816b1ded Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 18 May 2020 14:20:30 -0500 +Subject: [PATCH] wine.inf: Associate the "steam" protocol with winebrowser. + +--- + loader/wine.inf.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 247eb906422..0b014dce63c 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -317,6 +317,7 @@ HKCR,https\shell\open\ddeexec,"NoActivateHandler",2,"" + HKCR,http\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,https\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,mailto\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" ++HKCR,steam\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + + HKCR,MIME\Database\Charset\_iso-2022-jp$ESC,"Codepage",0x10003,932 + HKCR,MIME\Database\Charset\_iso-2022-jp$ESC,"InternetEncoding",0x10003,50221 + +From 4208548dbb789b3f3bd112a4dc1a19ebf0d1f307 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 22 Jan 2018 14:35:51 -0600 +Subject: [PATCH] HACK: wineboot: Don't show "updating prefix" window + +--- + programs/wineboot/wineboot.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index b85a3b6b6ea..6dfc797313e 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1035,6 +1035,7 @@ static INT_PTR CALLBACK wait_dlgproc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp + return 0; + } + ++/* + static HWND show_wait_window(void) + { + const char *config_dir = wine_get_config_dir(); +@@ -1051,6 +1052,7 @@ static HWND show_wait_window(void) + HeapFree( GetProcessHeap(), 0, name ); + return hwnd; + } ++*/ + + static HANDLE start_rundll32( const char *inf_path, BOOL wow64 ) + { +@@ -1127,7 +1129,7 @@ static void update_wineprefix( BOOL force ) + + if ((process = start_rundll32( inf_path, FALSE ))) + { +- HWND hwnd = show_wait_window(); ++/* HWND hwnd = show_wait_window();*/ + for (;;) + { + MSG msg; +@@ -1139,7 +1141,7 @@ static void update_wineprefix( BOOL force ) + } + else while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageW( &msg ); + } +- DestroyWindow( hwnd ); ++/* DestroyWindow( hwnd );*/ + } + WINE_MESSAGE( "wine: configuration in '%s' has been updated.\n", config_dir ); + } + +From 1d06745fc4cc9353b5e8bdb48d4cb716b2a7ec85 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 7 May 2021 16:50:29 +0300 +Subject: [PATCH] wine.inf: Associate the steam protocol with steam.exe. + +For 2K Launcher. + +CW-Bug-Id: 18912 +--- + loader/wine.inf.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index a2b45b3c28f..851a31b7d05 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -330,7 +330,7 @@ HKCR,https\shell\open\ddeexec,"NoActivateHandler",2,"" + HKCR,http\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,https\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" + HKCR,mailto\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" +-HKCR,steam\shell\open\command,,2,"""%11%\winebrowser.exe"" ""%1""" ++HKCR,steam\shell\open\command,,,"""%16426%\Steam\Steam.exe"" -- ""%1""" + + HKCR,MIME\Database\Charset\_iso-2022-jp$ESC,"Codepage",0x10003,932 + HKCR,MIME\Database\Charset\_iso-2022-jp$ESC,"InternetEncoding",0x10003,50221 +From 6e85dc3a77b5989d7ff78723cc2a8359c6e40d05 Mon Sep 17 00:00:00 2001 +From: Liam Middlebrook +Date: Tue, 11 May 2021 20:23:18 -0700 +Subject: [PATCH] loader: Set default regkey for NVIDIA NGX FullPath + +Sets the default location for the NVIDIA NGX SDK search-path to be +C:\Windows\System32\ + +This is required for supporting NVIDIA DLSS within Proton. + +Reviewed-by: Adam Moss +--- + loader/wine.inf.in | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 39887d3d84e..f4e0d7f50f5 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -74,7 +74,8 @@ AddReg=\ + Timezones,\ + VersionInfo,\ + LicenseInformation, \ +- SteamClient ++ SteamClient, \ ++ NVIDIANGX + + [DefaultInstall.ntamd64] + RegisterDlls=RegisterDllsSection +@@ -102,7 +103,8 @@ AddReg=\ + Timezones,\ + VersionInfo,\ + LicenseInformation, \ +- SteamClient.ntamd64 ++ SteamClient.ntamd64, \ ++ NVIDIANGX + + [DefaultInstall.ntarm64] + RegisterDlls=RegisterDllsSection +@@ -145,7 +147,8 @@ AddReg=\ + Tapi,\ + VersionInfo,\ + LicenseInformation, \ +- SteamClient ++ SteamClient, \ ++ NVIDIANGX + + [Wow64Install.ntarm64] + WineFakeDlls=FakeDllsWin32 +@@ -4261,6 +4264,9 @@ HKCU,Software\Wine\DllOverrides,"ucrtbase",0x2,"native,builtin" + HKCU,Software\Valve\Steam\ActiveProcess,"SteamClientDll64",,"%16426%\Steam\steamclient64.dll" + HKCU,Software\Valve\Steam\ActiveProcess,"SteamPath",,"%16426%\Steam" + HKLM,Software\Wow6432Node\Valve\Steam,"InstallPath",,"%16422%\Steam" ++ ++[NVIDIANGX] ++HKLM,Software\NVIDIA Corporation\Global\NGXCore,"FullPath",,"C:\Windows\System32" + + [NlsFiles] + c_037.nls diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits index f49cada03..b0fa55b18 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits @@ -1,7 +1,9 @@ #!/bin/bash - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 863858da2aa42edfa3b4e639905fcad7056eaeb5 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 7ada0e223904c563d79af97922dc8301ecf030f9 HEAD ); then _patchname='proton-steam-bits.patch' && _patchmsg="Using Steam-specific Proton-tkg patches (staging) 3/3" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 863858da2aa42edfa3b4e639905fcad7056eaeb5 HEAD ); then + _patchname='proton-steam-bits-7ada0e2.patch' && _patchmsg="Using Steam-specific Proton-tkg patches (staging) 3/3" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 656edbb508d51cbe3155d856ee3f2c27a6cd4cba HEAD ); then _patchname='proton-steam-bits-863858d.patch' && _patchmsg="Using Steam-specific Proton-tkg patches (staging) 3/3" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor bf811fdcaf70883634691d2c7262c3d80fe32323 HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits.patch index 5527aa326..660edbf58 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-steam-bits/proton-steam-bits.patch @@ -11,9 +11,9 @@ diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 5f9f61e2535..43cae2a2bc2 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in -@@ -73,7 +73,8 @@ AddReg=\ +@@ -96,7 +97,8 @@ AddReg=\ Tapi,\ - Timezones,\ + ThemeManager,\ VersionInfo,\ - LicenseInformation + LicenseInformation, \ @@ -21,24 +21,34 @@ index 5f9f61e2535..43cae2a2bc2 100644 [DefaultInstall.ntamd64] RegisterDlls=RegisterDllsSection -@@ -100,7 +101,8 @@ AddReg=\ +@@ -122,7 +124,8 @@ AddReg=\ Tapi,\ - Timezones,\ + ThemeManager,\ VersionInfo,\ - LicenseInformation + LicenseInformation, \ -+ SteamClient ++ SteamClient.ntamd64 [DefaultInstall.ntarm64] RegisterDlls=RegisterDllsSection -@@ -142,7 +144,8 @@ AddReg=\ +@@ -148,7 +150,8 @@ AddReg=\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ +- LicenseInformation ++ LicenseInformation, \ ++ SteamClient.ntamd64 + + [Wow64Install] + RegisterDlls=RegisterDllsSection +@@ -164,7 +166,8 @@ AddReg=\ Misc,\ Tapi,\ VersionInfo,\ - LicenseInformation + LicenseInformation, \ + SteamClient - + [DefaultInstall.Services] @@ -3867,6 +3870,22 @@ l_intl.nls @@ -516,9 +526,9 @@ index 5a26e1aef1f..d89a76272b0 100644 + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); +} + - static const struct - { - int type; + /****************************************************************** + * dbg_active_attach + * @@ -967,6 +1011,7 @@ enum dbg_start dbg_active_auto(int argc, char* argv[]) break; case TRUE: @@ -699,8 +709,8 @@ index 39887d3d84e..f4e0d7f50f5 100644 Timezones,\ VersionInfo,\ LicenseInformation, \ -- SteamClient -+ SteamClient, \ +- SteamClient.ntamd64 ++ SteamClient.ntamd64, \ + NVIDIANGX [DefaultInstall.ntarm64] diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-59485f0.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-59485f0.patch new file mode 100644 index 000000000..2688a130a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-59485f0.patch @@ -0,0 +1,848 @@ +From b9bb042502857bf088bdde3cdb0f998b6fbdcedc Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 13 Jun 2017 12:35:56 -0500 +Subject: [PATCH] HACK: steam: ntdll: Append C:/Program Files (x86)/Steam to + PATH. + +--- + dlls/ntdll/loader.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index bf072af84ac..15d6dde0377 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -84,7 +84,7 @@ const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + HMODULE kernel32_handle = 0; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +From 53ba023e0a3638123c1ae64a070b45b76fdedece Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 22 Nov 2022 11:02:52 +0100 +Subject: [PATCH] HACK: steam: ntdll: Setup steamclient trampolines to + lsteamclient. + +This uses exec page faults to jump from native steamclient into our +lsteamclient entry points. +--- + dlls/ntdll/loader.c | 23 +++++++++++ + dlls/ntdll/unix/loader.c | 71 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/signal_i386.c | 7 ++++ + dlls/ntdll/unix/signal_x86_64.c | 7 ++++ + dlls/ntdll/unix/unix_private.h | 3 ++ + dlls/ntdll/unixlib.h | 7 ++++ + 6 files changed, 118 insertions(+) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 81aca02ab40..327979dfe40 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -2107,12 +2107,16 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + DWORD flags, BOOL system, WINE_MODREF **pwm ) + { + static const char builtin_signature[] = "Wine builtin DLL"; ++ static HMODULE lsteamclient = NULL; + char *signature = (char *)((IMAGE_DOS_HEADER *)*module + 1); ++ UNICODE_STRING lsteamclient_us; + BOOL is_builtin; + IMAGE_NT_HEADERS *nt; + WINE_MODREF *wm; + NTSTATUS status; + SIZE_T map_size; ++ WCHAR *basename, *tmp; ++ ULONG basename_len; + + if (!(nt = RtlImageNtHeader( *module ))) return STATUS_INVALID_IMAGE_FORMAT; + +@@ -2133,6 +2137,25 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + + set_security_cookie( *module, map_size ); + ++ basename = nt_name->Buffer; ++ if ((tmp = wcsrchr(basename, '\\'))) basename = tmp + 1; ++ if ((tmp = wcsrchr(basename, '/'))) basename = tmp + 1; ++ basename_len = wcslen(basename); ++ if (basename_len >= 4 && !wcscmp(basename + basename_len - 4, L".dll")) basename_len -= 4; ++ ++ if ((!RtlCompareUnicodeStrings(basename, basename_len, L"steamclient", 11, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"steamclient64", 13, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer", 19, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer64", 21, TRUE)) && ++ RtlCreateUnicodeStringFromAsciiz(&lsteamclient_us, "lsteamclient.dll") && ++ (lsteamclient || LdrLoadDll(load_path, 0, &lsteamclient_us, &lsteamclient) == STATUS_SUCCESS)) ++ { ++ struct steamclient_setup_trampolines_params params = {.src_mod = *module, .tgt_mod = lsteamclient}; ++ WINE_UNIX_CALL( unix_steamclient_setup_trampolines, ¶ms ); ++ wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; ++ flags |= DONT_RESOLVE_DLL_REFERENCES; ++ } ++ + /* fixup imports */ + + if (!(flags & DONT_RESOLVE_DLL_REFERENCES) && +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index a675adea3e3..cf532b236ad 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1305,6 +1305,81 @@ static NTSTATUS load_so_dll( void *args ) + } + + ++static void *steamclient_srcs[128]; ++static void *steamclient_tgts[128]; ++static int steamclient_count; ++ ++void *steamclient_handle_fault( LPCVOID addr, DWORD err ) ++{ ++ int i; ++ ++ if (!(err & EXCEPTION_EXECUTE_FAULT)) return NULL; ++ ++ for (i = 0; i < steamclient_count; ++i) ++ { ++ if (addr == steamclient_srcs[i]) ++ return steamclient_tgts[i]; ++ } ++ ++ return NULL; ++} ++ ++static NTSTATUS steamclient_setup_trampolines( void *args ) ++{ ++ struct steamclient_setup_trampolines_params *params = args; ++ HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; ++ SYSTEM_BASIC_INFORMATION info; ++ IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); ++ IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); ++ IMAGE_SECTION_HEADER *src_sec = (IMAGE_SECTION_HEADER *)(src_nt + 1); ++ const IMAGE_EXPORT_DIRECTORY *src_exp, *tgt_exp; ++ const DWORD *names; ++ SIZE_T size; ++ void *addr, *src_addr, *tgt_addr; ++ char *name; ++ UINT_PTR page_mask; ++ int i; ++ ++ virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); ++ page_mask = info.PageSize - 1; ++ ++ for (i = 0; i < src_nt->FileHeader.NumberOfSections; ++i) ++ { ++ if (memcmp(src_sec[i].Name, ".text", 5)) continue; ++ addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); ++ size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; ++ mprotect(addr, size, PROT_READ); ++ } ++ ++ src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ tgt_exp = get_module_data_dir( tgt_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ names = (const DWORD *)((UINT_PTR)src_mod + src_exp->AddressOfNames); ++ for (i = 0; i < src_exp->NumberOfNames; ++i) ++ { ++ if (!names[i] || !(name = (char *)((UINT_PTR)src_mod + names[i]))) continue; ++ if (!(src_addr = (void *)find_named_export(src_mod, src_exp, name))) continue; ++ if (!(tgt_addr = (void *)find_named_export(tgt_mod, tgt_exp, name))) continue; ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++ } ++ ++ src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); ++ tgt_addr = (void *)((UINT_PTR)tgt_mod + tgt_nt->OptionalHeader.AddressOfEntryPoint); ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS unixcall_steamclient_setup_trampolines( void *args ) ++{ ++ return steamclient_setup_trampolines( args ); ++} ++ + static const unixlib_entry_t unix_call_funcs[] = + { + load_so_dll, +@@ -1315,6 +1315,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + unixcall_wine_server_handle_to_fd, + unixcall_wine_spawnvp, + system_time_precise, ++ unixcall_steamclient_setup_trampolines, + }; + + +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index d800885748f..aa0f10d5bfa 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1859,6 +1859,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + struct xcontext xcontext; + ucontext_t *ucontext = sigcontext; + void *stack = setup_exception_record( sigcontext, &rec, &xcontext ); ++ void *steamclient_addr = NULL; + + switch (TRAP_sig(ucontext)) + { +@@ -1893,6 +1894,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ EIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 94d79b9cdd1..b369988b85c 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -1966,6 +1966,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + EXCEPTION_RECORD rec = { 0 }; + struct xcontext context; + ucontext_t *ucontext = init_handler( sigcontext ); ++ void *steamclient_addr = NULL; + + rec.ExceptionAddress = (void *)RIP_sig(ucontext); + save_context( &context, ucontext ); +@@ -1997,6 +1998,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ RIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 347d1dd6102..975ac0b334f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -197,6 +197,9 @@ extern NTSTATUS system_time_precise( void *args ) DECLSPEC_HIDDEN; + + extern void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ) DECLSPEC_HIDDEN; + extern void *anon_mmap_alloc( size_t size, int prot ) DECLSPEC_HIDDEN; ++ ++extern void *steamclient_handle_fault( LPCVOID addr, DWORD err ) DECLSPEC_HIDDEN; ++ + extern void virtual_init(void) DECLSPEC_HIDDEN; + extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; + extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 0b4c2a984bb..36b0ded3bc9 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -38,17 +38,24 @@ struct unwind_builtin_dll_params + CONTEXT *context; + }; + ++struct steamclient_setup_trampolines_params ++{ ++ HMODULE src_mod; ++ HMODULE tgt_mod; ++}; ++ + enum ntdll_unix_funcs + { + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, + unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, + unix_wine_spawnvp, + unix_system_time_precise, ++ unix_steamclient_setup_trampolines, + }; + + extern unixlib_handle_t __wine_unixlib_handle DECLSPEC_HIDDEN; +From 92e01e33fe6eb993582ba40e931479d78f4e9d60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 5 Aug 2020 10:35:50 +0200 +Subject: [PATCH] HACK: steam: ntdll: Patch entry points with jumps. + +As a preferred alternative to noexec pages which makes debugging +painful. The noexec can be enabled with WINESTEAMNOEXEC=1 environmnent +variable. +--- + dlls/ntdll/unix/loader.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index cf532b236ad..3d250b72178 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2062,8 +2062,22 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + ++static void steamclient_write_jump(void *src_addr, void *tgt_addr) ++{ ++#ifdef _WIN64 ++ static const char mov[] = {0x48, 0xb8}; ++#else ++ static const char mov[] = {0xb8}; ++#endif ++ static const char jmp[] = {0xff, 0xe0}; ++ memcpy(src_addr, mov, sizeof(mov)); ++ memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); ++ memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); ++} ++ + static NTSTATUS steamclient_setup_trampolines( void *args ) + { ++ static int noexec_cached = -1; + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; +@@ -2074,10 +2088,13 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name; ++ char *name, *wsne; + UINT_PTR page_mask; + int i; + ++ if (noexec_cached == -1) ++ noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); ++ + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2086,7 +2103,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- mprotect(addr, size, PROT_READ); ++ if (noexec_cached) mprotect(addr, size, PROT_READ); ++ else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2100,7 +2118,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2108,7 +2127,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + + return STATUS_SUCCESS; + } +From 901e614e8f3d8913e7f75ccd6cdbabbd0502c53f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 18 Dec 2019 13:49:00 +0100 +Subject: [PATCH] HACK: proton: ntdll: Strip gameoverlayrenderer.so from + LD_PRELOAD before executing explorer.exe. + +Work around a bug in gameoverlayrenderer which introduces 50ms hangs +during XCheckIfEvent after approx 40 minutes of gameplay. + +The original user32 hack broke Steam overlay in Origin games, and Steam +Input consequently. This ntdll implementation should be safer as it'll +modify the environment after the new process has started forking. + +Link: https://github.com/ValveSoftware/Proton/issues/3316 +CW-Bug-Id: #18946 +--- + dlls/ntdll/unix/loader.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index a7e79a828ca..3df74d45bd7 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -725,6 +725,7 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; ++ const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; +@@ -759,6 +760,36 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + else loader = is_child_64bit ? "wine64" : "wine"; + } + ++ /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ ++ if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && ++ argv[3] && !strcmp( argv[3], "/desktop" )) ++ { ++ static char const gorso[] = "gameoverlayrenderer.so"; ++ static int gorso_len = sizeof(gorso) - 1; ++ int len = strlen( ld_preload ); ++ char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); ++ ++ if (!env) return STATUS_NO_MEMORY; ++ strcpy( env, "LD_PRELOAD=" ); ++ strcat( env, ld_preload ); ++ ++ tmp = env + 11; ++ do ++ { ++ if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); ++ if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) ++ { ++ if (*next) memmove( tmp, next + 1, strlen(next) ); ++ else *tmp = 0; ++ next = tmp; ++ } ++ else tmp = next + 1; ++ } ++ while (*next); ++ ++ putenv( env ); ++ } ++ + signal( SIGPIPE, SIG_DFL ); + + sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); +From 08290ccabc137d205ceec261706d78b021d14fa4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 22 Nov 2022 11:08:22 +0100 +Subject: [PATCH] HACK: proton: ntdll: Export a function to set a Unix + environment variable + +--- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/env.c | 10 +++ + dlls/ntdll/unix/loader.c | 1 + + dlls/ntdll/unix/unix_private.h | 1 + + tools/gdbinit.py | 113 +++++++++++++++++++++++++++++++++ + 5 files changed, 126 insertions(+) + create mode 100644 tools/gdbinit.py + +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index b41e29c0ff5..05999131fc5 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1696,6 +1696,7 @@ + @ extern -private __wine_syscall_dispatcher + @ extern -private __wine_unix_call_dispatcher + @ extern -private __wine_unixlib_handle ++@ stdcall -syscall __wine_set_unix_env(ptr ptr) + + # Debugging + @ stdcall -syscall -norelay __wine_dbg_write(ptr long) +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index 6f1709f4713..3c387ba9027 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -2418,3 +2418,13 @@ void WINAPI RtlSetLastWin32Error( DWORD err ) + #endif + teb->LastErrorValue = err; + } ++ ++ ++/********************************************************************** ++ * __wine_set_unix_env (ntdll.so) ++ */ ++ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ) ++{ ++ setenv(var, val, 1); ++ return 0; ++} +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 3d250b72178..fac93f815fd 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -357,6 +357,7 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_set_unix_env, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 975ac0b334f..a723ae3c5b9 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -294,6 +294,7 @@ extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; + extern void call_raise_user_exception_dispatcher(void) DECLSPEC_HIDDEN; ++extern ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ) DECLSPEC_HIDDEN; + + #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */ + +diff --git a/tools/gdbinit.py b/tools/gdbinit.py +new file mode 100644 +index 00000000000..ba3b7d003ac +--- /dev/null ++++ b/tools/gdbinit.py +@@ -0,0 +1,113 @@ ++#!/bin/env python3 ++ ++# Copyright 2021 RĂ©mi Bernon 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 ++ ++from __future__ import print_function ++ ++import gdb ++import re ++import subprocess ++import sys ++ ++class LoadSymbolFiles(gdb.Command): ++ 'Command to load symbol files directly from /proc//maps.' ++ ++ def __init__(self): ++ sup = super(LoadSymbolFiles, self) ++ sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE, ++ False) ++ ++ self.libs = {} ++ gdb.execute('alias -a lsf = load-symbol-files', True) ++ ++ def invoke(self, arg, from_tty): ++ pid = gdb.selected_inferior().pid ++ if not pid in self.libs: self.libs[pid] = {} ++ ++ def command(cmd, confirm=from_tty, to_string=not from_tty): ++ gdb.execute(cmd, from_tty=confirm, to_string=to_string) ++ ++ def execute(cmd): ++ return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \ ++ .decode('utf-8') ++ ++ # load mappings addresses ++ libs = {} ++ with open('/proc/{}/maps'.format(pid), 'r') as maps: ++ for line in maps: ++ addr, _, _, _, node, path = re.split(r'\s+', line, 5) ++ path = path.strip() ++ if node == '0': continue ++ if path in libs: continue ++ libs[path] = int(addr.split('-')[0], 16) ++ ++ # unload symbol file if address changed ++ for k in set(libs) & set(self.libs[pid]): ++ if libs[k] != self.libs[pid][k]: ++ command('remove-symbol-file "{}"'.format(k), confirm=False) ++ del self.libs[k] ++ ++ # load symbol file for new mappings ++ for k in set(libs) - set(self.libs[pid]): ++ if arg is not None and re.search(arg, k) is None: continue ++ addr = self.libs[pid][k] = libs[k] ++ has_debug = False ++ offs = None ++ ++ try: ++ out = execute(['file', k]) ++ except: ++ continue ++ ++ # try loading mapping as ELF ++ try: ++ out = execute(['readelf', '-l', k]) ++ for line in out.split('\n'): ++ if not 'LOAD' in line: continue ++ base = int(line.split()[2], 16) ++ break ++ except: ++ # assume mapping is PE ++ base = -1 ++ ++ try: ++ name = None ++ cmd = 'add-symbol-file "{}"'.format(k) ++ out = execute(['objdump', '-h', k]) ++ for line in out.split('\n'): ++ if '2**' in line: ++ _, name, _, vma, _, off, _ = line.split(maxsplit=6) ++ if base < 0: offs = int(off, 16) ++ else: offs = int(vma, 16) - base ++ if 'ALLOC' in line: ++ cmd += ' -s {} 0x{:x}'.format(name, addr + offs) ++ elif name in ['.gnu_debuglink', '.debug_info']: ++ has_debug = True ++ elif 'DEBUGGING' in line: ++ has_debug = True ++ except: ++ continue ++ ++ if not has_debug: ++ print('no debugging info found in {}'.format(k)) ++ continue ++ ++ print('loading symbols for {}'.format(k)) ++ command(cmd, confirm=False, to_string=True) ++ ++ ++LoadSymbolFiles() + +From 7aa2679cea3e6edb206da42d23d9d12cb0f1e937 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 21 May 2021 21:54:39 +0200 +Subject: [PATCH] ntdll: Set RLIMIT_NICE to its hard limit and inform the + server. + +--- + dlls/ntdll/unix/loader.c | 3 +++ + dlls/ntdll/unix/server.c | 14 ++++++++++++++ + server/process.h | 1 + + server/protocol.def | 1 + + server/thread.c | 7 +++++++ + 5 files changed, 26 insertions(+) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index c46f109af71..7ece87338fc 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2548,6 +2548,9 @@ void __wine_main( int argc, char *argv[], char *envp[] ) + #ifdef RLIMIT_AS + set_max_limit( RLIMIT_AS ); + #endif ++#ifdef RLIMIT_NICE ++ set_max_limit( RLIMIT_NICE ); ++#endif + + virtual_init(); + init_environment( argc, argv, envp ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 56a110882ed..321b91d20e0 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -53,6 +53,9 @@ + # include + #endif + #include ++#ifdef HAVE_SYS_RESOURCE_H ++# include ++#endif + #ifdef HAVE_SYS_SYSCALL_H + # include + #endif +@@ -1455,6 +1458,8 @@ size_t server_init_process(void) + struct sigaction sig_act; + size_t info_size; + DWORD pid, tid; ++ struct rlimit rlimit; ++ int nice_limit = 0; + + server_pid = -1; + if (env_socket) +@@ -1516,10 +1521,19 @@ size_t server_init_process(void) + + reply_pipe = init_thread_pipe(); + ++#ifdef RLIMIT_NICE ++ if (!getrlimit( RLIMIT_NICE, &rlimit )) ++ { ++ if (rlimit.rlim_cur <= 40) nice_limit = 20 - rlimit.rlim_cur; ++ else if (rlimit.rlim_cur == -1 /* RLIMIT_INFINITY */) nice_limit = -20; ++ } ++#endif ++ + SERVER_START_REQ( init_first_thread ) + { + req->unix_pid = getpid(); + req->unix_tid = get_unix_tid(); ++ req->nice_limit = nice_limit; + req->reply_fd = reply_pipe; + req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; + req->debug_level = (TRACE_ON(server) != 0); +diff --git a/server/process.h b/server/process.h +index a0a071d8f88..d073e9e285f 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -50,6 +50,7 @@ struct process + timeout_t sigkill_delay; /* delay before final SIGKILL */ + unsigned short machine; /* client machine type */ + int unix_pid; /* Unix pid for final SIGKILL */ ++ int nice_limit; /* RLIMIT_NICE of the process */ + int exit_code; /* process exit code */ + int running_threads; /* number of threads running in this process */ + timeout_t start_time; /* absolute time at process start */ +diff --git a/server/protocol.def b/server/protocol.def +index 880ad794982..42eae1db9f3 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -941,6 +941,7 @@ typedef struct + int debug_level; /* new debug level */ + int reply_fd; /* fd for reply pipe */ + int wait_fd; /* fd for blocking calls pipe */ ++ char nice_limit; /* RLIMIT_NICE of new thread */ + @REPLY + process_id_t pid; /* process id of the new thread's process */ + thread_id_t tid; /* thread id of the new thread */ +diff --git a/server/thread.c b/server/thread.c +index 2cb45e2bf76..7efa00312bb 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -37,6 +37,12 @@ + #define _WITH_CPU_SET_T + #include + #endif ++#ifdef HAVE_SYS_TIME_H ++#include ++#endif ++#ifdef HAVE_SYS_RESOURCE_H ++#include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -1489,6 +1495,7 @@ DECL_HANDLER(init_first_thread) + + current->unix_pid = process->unix_pid = req->unix_pid; + current->unix_tid = req->unix_tid; ++ process->nice_limit = req->nice_limit; + + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); + +From cbf6d8c3fe33c73db63246675e2a0e2672128465 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 11 Oct 2021 10:58:33 +0200 +Subject: [PATCH] ntdll: Use RTLD_NOLOAD to find already mapped modules. + +This makes it possible to detect modules that weren't unmapped from +dlclose, and that we should not fixup again. +--- + dlls/ntdll/unix/loader.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 7ece87338fc..55d36eaec4f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1299,8 +1299,10 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + { + void *module, *handle; + const IMAGE_NT_HEADERS *nt; ++ BOOL mapped = FALSE; + +- handle = dlopen( so_name, RTLD_NOW ); ++ if ((handle = dlopen( so_name, RTLD_NOW | RTLD_NOLOAD ))) mapped = TRUE; ++ else handle = dlopen( so_name, RTLD_NOW ); + if (!handle) + { + WARN( "failed to load .so lib %s: %s\n", debugstr_a(so_name), dlerror() ); +@@ -1318,7 +1320,7 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + return STATUS_SUCCESS; + } + +- if (map_so_dll( nt, module )) ++ if (!mapped && map_so_dll( nt, module )) + { + dlclose( handle ); + return STATUS_NO_MEMORY; +From 4fd446bd2e3e289ff792c5315d8291fbcad7aca5 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 May 2022 13:17:32 -0500 +Subject: [PATCH] ntdll: Disable 16-bit TIB hack + +MechWarrior Online (342200) writes directly to the SubSystemTib field, +which Wine would interpret to mean it's a 16-bit executable and then +crash. We're unlikely to run into any real 16-bit applications in Proton +(they won't work on modern Windows, anyway), so let's just disable that +hack entirely. + +CW-Bug-Id: #20673 +--- + dlls/kernelbase/loader.c | 2 +- + dlls/ntdll/env.c | 2 +- + dlls/ntdll/path.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c +index 0fd2d7b7c99..af3c193f331 100644 +--- a/dlls/kernelbase/loader.c ++++ b/dlls/kernelbase/loader.c +@@ -302,7 +302,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameW( HMODULE module, LPWSTR filena + UNICODE_STRING name; + NTSTATUS status; + +- if (!module && ((win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) ++ if (!module && (0 && (win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) + { + len = min( size, win16_tib->exe_name->Length / sizeof(WCHAR) ); + memcpy( filename, win16_tib->exe_name->Buffer, len * sizeof(WCHAR) ); +diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c +index 6db6cee17cd..7d993cd799c 100644 +--- a/dlls/ntdll/env.c ++++ b/dlls/ntdll/env.c +@@ -589,7 +589,7 @@ NTSTATUS WINAPI RtlCreateProcessParametersEx( RTL_USER_PROCESS_PARAMETERS **resu + if (!DllPath) DllPath = &null_str; + if (!CurrentDirectoryName) + { +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = ((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + curdir = cur_params->CurrentDirectory.DosPath; +diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c +index da6f55ddb83..dda6ba4ee53 100644 +--- a/dlls/ntdll/path.c ++++ b/dlls/ntdll/path.c +@@ -528,7 +528,7 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + cd = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -883,7 +883,7 @@ ULONG WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + us = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -927,7 +927,7 @@ NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir; + else + curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory; + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-7f088b0.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-7f088b0.patch new file mode 100644 index 000000000..6cb8daf07 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-7f088b0.patch @@ -0,0 +1,848 @@ +From b9bb042502857bf088bdde3cdb0f998b6fbdcedc Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 13 Jun 2017 12:35:56 -0500 +Subject: [PATCH] HACK: steam: ntdll: Append C:/Program Files (x86)/Steam to + PATH. + +--- + dlls/ntdll/loader.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index bf072af84ac..15d6dde0377 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -84,7 +84,7 @@ const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + HMODULE kernel32_handle = 0; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +From 53ba023e0a3638123c1ae64a070b45b76fdedece Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 22 Nov 2022 11:02:52 +0100 +Subject: [PATCH] HACK: steam: ntdll: Setup steamclient trampolines to + lsteamclient. + +This uses exec page faults to jump from native steamclient into our +lsteamclient entry points. +--- + dlls/ntdll/loader.c | 23 +++++++++++ + dlls/ntdll/unix/loader.c | 71 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/signal_i386.c | 7 ++++ + dlls/ntdll/unix/signal_x86_64.c | 7 ++++ + dlls/ntdll/unix/unix_private.h | 3 ++ + dlls/ntdll/unixlib.h | 7 ++++ + 6 files changed, 118 insertions(+) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 81aca02ab40..327979dfe40 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -2107,12 +2107,16 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + DWORD flags, BOOL system, WINE_MODREF **pwm ) + { + static const char builtin_signature[] = "Wine builtin DLL"; ++ static HMODULE lsteamclient = NULL; + char *signature = (char *)((IMAGE_DOS_HEADER *)*module + 1); ++ UNICODE_STRING lsteamclient_us; + BOOL is_builtin; + IMAGE_NT_HEADERS *nt; + WINE_MODREF *wm; + NTSTATUS status; + SIZE_T map_size; ++ WCHAR *basename, *tmp; ++ ULONG basename_len; + + if (!(nt = RtlImageNtHeader( *module ))) return STATUS_INVALID_IMAGE_FORMAT; + +@@ -2133,6 +2137,25 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + + set_security_cookie( *module, map_size ); + ++ basename = nt_name->Buffer; ++ if ((tmp = wcsrchr(basename, '\\'))) basename = tmp + 1; ++ if ((tmp = wcsrchr(basename, '/'))) basename = tmp + 1; ++ basename_len = wcslen(basename); ++ if (basename_len >= 4 && !wcscmp(basename + basename_len - 4, L".dll")) basename_len -= 4; ++ ++ if ((!RtlCompareUnicodeStrings(basename, basename_len, L"steamclient", 11, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"steamclient64", 13, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer", 19, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer64", 21, TRUE)) && ++ RtlCreateUnicodeStringFromAsciiz(&lsteamclient_us, "lsteamclient.dll") && ++ (lsteamclient || LdrLoadDll(load_path, 0, &lsteamclient_us, &lsteamclient) == STATUS_SUCCESS)) ++ { ++ struct steamclient_setup_trampolines_params params = {.src_mod = *module, .tgt_mod = lsteamclient}; ++ WINE_UNIX_CALL( unix_steamclient_setup_trampolines, ¶ms ); ++ wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; ++ flags |= DONT_RESOLVE_DLL_REFERENCES; ++ } ++ + /* fixup imports */ + + if (!(flags & DONT_RESOLVE_DLL_REFERENCES) && +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index a675adea3e3..cf532b236ad 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1305,6 +1305,81 @@ static NTSTATUS load_so_dll( void *args ) + } + + ++static void *steamclient_srcs[128]; ++static void *steamclient_tgts[128]; ++static int steamclient_count; ++ ++void *steamclient_handle_fault( LPCVOID addr, DWORD err ) ++{ ++ int i; ++ ++ if (!(err & EXCEPTION_EXECUTE_FAULT)) return NULL; ++ ++ for (i = 0; i < steamclient_count; ++i) ++ { ++ if (addr == steamclient_srcs[i]) ++ return steamclient_tgts[i]; ++ } ++ ++ return NULL; ++} ++ ++static NTSTATUS steamclient_setup_trampolines( void *args ) ++{ ++ struct steamclient_setup_trampolines_params *params = args; ++ HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; ++ SYSTEM_BASIC_INFORMATION info; ++ IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); ++ IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); ++ IMAGE_SECTION_HEADER *src_sec = (IMAGE_SECTION_HEADER *)(src_nt + 1); ++ const IMAGE_EXPORT_DIRECTORY *src_exp, *tgt_exp; ++ const DWORD *names; ++ SIZE_T size; ++ void *addr, *src_addr, *tgt_addr; ++ char *name; ++ UINT_PTR page_mask; ++ int i; ++ ++ virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); ++ page_mask = info.PageSize - 1; ++ ++ for (i = 0; i < src_nt->FileHeader.NumberOfSections; ++i) ++ { ++ if (memcmp(src_sec[i].Name, ".text", 5)) continue; ++ addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); ++ size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; ++ mprotect(addr, size, PROT_READ); ++ } ++ ++ src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ tgt_exp = get_module_data_dir( tgt_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ names = (const DWORD *)((UINT_PTR)src_mod + src_exp->AddressOfNames); ++ for (i = 0; i < src_exp->NumberOfNames; ++i) ++ { ++ if (!names[i] || !(name = (char *)((UINT_PTR)src_mod + names[i]))) continue; ++ if (!(src_addr = (void *)find_named_export(src_mod, src_exp, name))) continue; ++ if (!(tgt_addr = (void *)find_named_export(tgt_mod, tgt_exp, name))) continue; ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++ } ++ ++ src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); ++ tgt_addr = (void *)((UINT_PTR)tgt_mod + tgt_nt->OptionalHeader.AddressOfEntryPoint); ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS unixcall_steamclient_setup_trampolines( void *args ) ++{ ++ return steamclient_setup_trampolines( args ); ++} ++ + static const unixlib_entry_t unix_call_funcs[] = + { + load_so_dll, +@@ -1315,6 +1315,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + unixcall_wine_server_handle_to_fd, + unixcall_wine_spawnvp, + system_time_precise, ++ unixcall_steamclient_setup_trampolines, + }; + + +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index d800885748f..aa0f10d5bfa 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1859,6 +1859,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + struct xcontext xcontext; + ucontext_t *ucontext = sigcontext; + void *stack = setup_exception_record( sigcontext, &rec, &xcontext ); ++ void *steamclient_addr = NULL; + + switch (TRAP_sig(ucontext)) + { +@@ -1893,6 +1894,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ EIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 94d79b9cdd1..b369988b85c 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -1966,6 +1966,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + EXCEPTION_RECORD rec = { 0 }; + struct xcontext context; + ucontext_t *ucontext = init_handler( sigcontext ); ++ void *steamclient_addr = NULL; + + rec.ExceptionAddress = (void *)RIP_sig(ucontext); + save_context( &context, ucontext ); +@@ -1997,6 +1998,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ RIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 347d1dd6102..975ac0b334f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -197,6 +197,9 @@ extern NTSTATUS system_time_precise( void *args ) DECLSPEC_HIDDEN; + + extern void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ) DECLSPEC_HIDDEN; + extern void *anon_mmap_alloc( size_t size, int prot ) DECLSPEC_HIDDEN; ++ ++extern void *steamclient_handle_fault( LPCVOID addr, DWORD err ) DECLSPEC_HIDDEN; ++ + extern void virtual_init(void) DECLSPEC_HIDDEN; + extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; + extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 0b4c2a984bb..36b0ded3bc9 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -38,17 +38,24 @@ struct unwind_builtin_dll_params + CONTEXT *context; + }; + ++struct steamclient_setup_trampolines_params ++{ ++ HMODULE src_mod; ++ HMODULE tgt_mod; ++}; ++ + enum ntdll_unix_funcs + { + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, + unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, + unix_wine_spawnvp, + unix_system_time_precise, ++ unix_steamclient_setup_trampolines, + }; + + extern unixlib_handle_t __wine_unixlib_handle DECLSPEC_HIDDEN; +From 92e01e33fe6eb993582ba40e931479d78f4e9d60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 5 Aug 2020 10:35:50 +0200 +Subject: [PATCH] HACK: steam: ntdll: Patch entry points with jumps. + +As a preferred alternative to noexec pages which makes debugging +painful. The noexec can be enabled with WINESTEAMNOEXEC=1 environmnent +variable. +--- + dlls/ntdll/unix/loader.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index cf532b236ad..3d250b72178 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2062,8 +2062,22 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + ++static void steamclient_write_jump(void *src_addr, void *tgt_addr) ++{ ++#ifdef _WIN64 ++ static const char mov[] = {0x48, 0xb8}; ++#else ++ static const char mov[] = {0xb8}; ++#endif ++ static const char jmp[] = {0xff, 0xe0}; ++ memcpy(src_addr, mov, sizeof(mov)); ++ memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); ++ memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); ++} ++ + static NTSTATUS steamclient_setup_trampolines( void *args ) + { ++ static int noexec_cached = -1; + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; +@@ -2074,10 +2088,13 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name; ++ char *name, *wsne; + UINT_PTR page_mask; + int i; + ++ if (noexec_cached == -1) ++ noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); ++ + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2086,7 +2103,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- mprotect(addr, size, PROT_READ); ++ if (noexec_cached) mprotect(addr, size, PROT_READ); ++ else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2100,7 +2118,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2108,7 +2127,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + + return STATUS_SUCCESS; + } +From 901e614e8f3d8913e7f75ccd6cdbabbd0502c53f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 18 Dec 2019 13:49:00 +0100 +Subject: [PATCH] HACK: proton: ntdll: Strip gameoverlayrenderer.so from + LD_PRELOAD before executing explorer.exe. + +Work around a bug in gameoverlayrenderer which introduces 50ms hangs +during XCheckIfEvent after approx 40 minutes of gameplay. + +The original user32 hack broke Steam overlay in Origin games, and Steam +Input consequently. This ntdll implementation should be safer as it'll +modify the environment after the new process has started forking. + +Link: https://github.com/ValveSoftware/Proton/issues/3316 +CW-Bug-Id: #18946 +--- + dlls/ntdll/unix/loader.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index a7e79a828ca..3df74d45bd7 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -725,6 +725,7 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; ++ const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; +@@ -759,6 +760,36 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + else loader = is_child_64bit ? "wine64" : "wine"; + } + ++ /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ ++ if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && ++ argv[3] && !strcmp( argv[3], "/desktop" )) ++ { ++ static char const gorso[] = "gameoverlayrenderer.so"; ++ static int gorso_len = sizeof(gorso) - 1; ++ int len = strlen( ld_preload ); ++ char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); ++ ++ if (!env) return STATUS_NO_MEMORY; ++ strcpy( env, "LD_PRELOAD=" ); ++ strcat( env, ld_preload ); ++ ++ tmp = env + 11; ++ do ++ { ++ if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); ++ if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) ++ { ++ if (*next) memmove( tmp, next + 1, strlen(next) ); ++ else *tmp = 0; ++ next = tmp; ++ } ++ else tmp = next + 1; ++ } ++ while (*next); ++ ++ putenv( env ); ++ } ++ + signal( SIGPIPE, SIG_DFL ); + + sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); +From 08290ccabc137d205ceec261706d78b021d14fa4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 22 Nov 2022 11:08:22 +0100 +Subject: [PATCH] HACK: proton: ntdll: Export a function to set a Unix + environment variable + +--- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/env.c | 10 +++ + dlls/ntdll/unix/loader.c | 1 + + dlls/ntdll/unix/unix_private.h | 1 + + tools/gdbinit.py | 113 +++++++++++++++++++++++++++++++++ + 5 files changed, 126 insertions(+) + create mode 100644 tools/gdbinit.py + +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index b41e29c0ff5..05999131fc5 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1696,6 +1696,7 @@ + @ extern -private __wine_syscall_dispatcher + @ extern -private __wine_unix_call_dispatcher + @ extern -arch=arm64 __wine_current_teb ++@ stdcall -syscall __wine_set_unix_env(ptr ptr) + + # Debugging + @ stdcall -syscall -norelay __wine_dbg_write(ptr long) +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index 6f1709f4713..3c387ba9027 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -2418,3 +2418,13 @@ void WINAPI RtlSetLastWin32Error( DWORD err ) + #endif + teb->LastErrorValue = err; + } ++ ++ ++/********************************************************************** ++ * __wine_set_unix_env (ntdll.so) ++ */ ++ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ) ++{ ++ setenv(var, val, 1); ++ return 0; ++} +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 3d250b72178..fac93f815fd 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -357,6 +357,7 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_set_unix_env, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 975ac0b334f..a723ae3c5b9 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -294,6 +294,7 @@ extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, + PNTAPCFUNC func, NTSTATUS status ) DECLSPEC_HIDDEN; + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; + extern void call_raise_user_exception_dispatcher(void) DECLSPEC_HIDDEN; ++extern ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ) DECLSPEC_HIDDEN; + + #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */ + +diff --git a/tools/gdbinit.py b/tools/gdbinit.py +new file mode 100644 +index 00000000000..ba3b7d003ac +--- /dev/null ++++ b/tools/gdbinit.py +@@ -0,0 +1,113 @@ ++#!/bin/env python3 ++ ++# Copyright 2021 RĂ©mi Bernon 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 ++ ++from __future__ import print_function ++ ++import gdb ++import re ++import subprocess ++import sys ++ ++class LoadSymbolFiles(gdb.Command): ++ 'Command to load symbol files directly from /proc//maps.' ++ ++ def __init__(self): ++ sup = super(LoadSymbolFiles, self) ++ sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE, ++ False) ++ ++ self.libs = {} ++ gdb.execute('alias -a lsf = load-symbol-files', True) ++ ++ def invoke(self, arg, from_tty): ++ pid = gdb.selected_inferior().pid ++ if not pid in self.libs: self.libs[pid] = {} ++ ++ def command(cmd, confirm=from_tty, to_string=not from_tty): ++ gdb.execute(cmd, from_tty=confirm, to_string=to_string) ++ ++ def execute(cmd): ++ return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \ ++ .decode('utf-8') ++ ++ # load mappings addresses ++ libs = {} ++ with open('/proc/{}/maps'.format(pid), 'r') as maps: ++ for line in maps: ++ addr, _, _, _, node, path = re.split(r'\s+', line, 5) ++ path = path.strip() ++ if node == '0': continue ++ if path in libs: continue ++ libs[path] = int(addr.split('-')[0], 16) ++ ++ # unload symbol file if address changed ++ for k in set(libs) & set(self.libs[pid]): ++ if libs[k] != self.libs[pid][k]: ++ command('remove-symbol-file "{}"'.format(k), confirm=False) ++ del self.libs[k] ++ ++ # load symbol file for new mappings ++ for k in set(libs) - set(self.libs[pid]): ++ if arg is not None and re.search(arg, k) is None: continue ++ addr = self.libs[pid][k] = libs[k] ++ has_debug = False ++ offs = None ++ ++ try: ++ out = execute(['file', k]) ++ except: ++ continue ++ ++ # try loading mapping as ELF ++ try: ++ out = execute(['readelf', '-l', k]) ++ for line in out.split('\n'): ++ if not 'LOAD' in line: continue ++ base = int(line.split()[2], 16) ++ break ++ except: ++ # assume mapping is PE ++ base = -1 ++ ++ try: ++ name = None ++ cmd = 'add-symbol-file "{}"'.format(k) ++ out = execute(['objdump', '-h', k]) ++ for line in out.split('\n'): ++ if '2**' in line: ++ _, name, _, vma, _, off, _ = line.split(maxsplit=6) ++ if base < 0: offs = int(off, 16) ++ else: offs = int(vma, 16) - base ++ if 'ALLOC' in line: ++ cmd += ' -s {} 0x{:x}'.format(name, addr + offs) ++ elif name in ['.gnu_debuglink', '.debug_info']: ++ has_debug = True ++ elif 'DEBUGGING' in line: ++ has_debug = True ++ except: ++ continue ++ ++ if not has_debug: ++ print('no debugging info found in {}'.format(k)) ++ continue ++ ++ print('loading symbols for {}'.format(k)) ++ command(cmd, confirm=False, to_string=True) ++ ++ ++LoadSymbolFiles() + +From 7aa2679cea3e6edb206da42d23d9d12cb0f1e937 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 21 May 2021 21:54:39 +0200 +Subject: [PATCH] ntdll: Set RLIMIT_NICE to its hard limit and inform the + server. + +--- + dlls/ntdll/unix/loader.c | 3 +++ + dlls/ntdll/unix/server.c | 14 ++++++++++++++ + server/process.h | 1 + + server/protocol.def | 1 + + server/thread.c | 7 +++++++ + 5 files changed, 26 insertions(+) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index c46f109af71..7ece87338fc 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2548,6 +2548,9 @@ void __wine_main( int argc, char *argv[], char *envp[] ) + #ifdef RLIMIT_AS + set_max_limit( RLIMIT_AS ); + #endif ++#ifdef RLIMIT_NICE ++ set_max_limit( RLIMIT_NICE ); ++#endif + + virtual_init(); + init_environment( argc, argv, envp ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 56a110882ed..321b91d20e0 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -53,6 +53,9 @@ + # include + #endif + #include ++#ifdef HAVE_SYS_RESOURCE_H ++# include ++#endif + #ifdef HAVE_SYS_SYSCALL_H + # include + #endif +@@ -1455,6 +1458,8 @@ size_t server_init_process(void) + struct sigaction sig_act; + size_t info_size; + DWORD pid, tid; ++ struct rlimit rlimit; ++ int nice_limit = 0; + + server_pid = -1; + if (env_socket) +@@ -1516,10 +1521,19 @@ size_t server_init_process(void) + + reply_pipe = init_thread_pipe(); + ++#ifdef RLIMIT_NICE ++ if (!getrlimit( RLIMIT_NICE, &rlimit )) ++ { ++ if (rlimit.rlim_cur <= 40) nice_limit = 20 - rlimit.rlim_cur; ++ else if (rlimit.rlim_cur == -1 /* RLIMIT_INFINITY */) nice_limit = -20; ++ } ++#endif ++ + SERVER_START_REQ( init_first_thread ) + { + req->unix_pid = getpid(); + req->unix_tid = get_unix_tid(); ++ req->nice_limit = nice_limit; + req->reply_fd = reply_pipe; + req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; + req->debug_level = (TRACE_ON(server) != 0); +diff --git a/server/process.h b/server/process.h +index a0a071d8f88..d073e9e285f 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -50,6 +50,7 @@ struct process + timeout_t sigkill_delay; /* delay before final SIGKILL */ + unsigned short machine; /* client machine type */ + int unix_pid; /* Unix pid for final SIGKILL */ ++ int nice_limit; /* RLIMIT_NICE of the process */ + int exit_code; /* process exit code */ + int running_threads; /* number of threads running in this process */ + timeout_t start_time; /* absolute time at process start */ +diff --git a/server/protocol.def b/server/protocol.def +index 880ad794982..42eae1db9f3 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -941,6 +941,7 @@ typedef struct + int debug_level; /* new debug level */ + int reply_fd; /* fd for reply pipe */ + int wait_fd; /* fd for blocking calls pipe */ ++ char nice_limit; /* RLIMIT_NICE of new thread */ + @REPLY + process_id_t pid; /* process id of the new thread's process */ + thread_id_t tid; /* thread id of the new thread */ +diff --git a/server/thread.c b/server/thread.c +index 2cb45e2bf76..7efa00312bb 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -37,6 +37,12 @@ + #define _WITH_CPU_SET_T + #include + #endif ++#ifdef HAVE_SYS_TIME_H ++#include ++#endif ++#ifdef HAVE_SYS_RESOURCE_H ++#include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -1489,6 +1495,7 @@ DECL_HANDLER(init_first_thread) + + current->unix_pid = process->unix_pid = req->unix_pid; + current->unix_tid = req->unix_tid; ++ process->nice_limit = req->nice_limit; + + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); + +From cbf6d8c3fe33c73db63246675e2a0e2672128465 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 11 Oct 2021 10:58:33 +0200 +Subject: [PATCH] ntdll: Use RTLD_NOLOAD to find already mapped modules. + +This makes it possible to detect modules that weren't unmapped from +dlclose, and that we should not fixup again. +--- + dlls/ntdll/unix/loader.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 7ece87338fc..55d36eaec4f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1299,8 +1299,10 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + { + void *module, *handle; + const IMAGE_NT_HEADERS *nt; ++ BOOL mapped = FALSE; + +- handle = dlopen( so_name, RTLD_NOW ); ++ if ((handle = dlopen( so_name, RTLD_NOW | RTLD_NOLOAD ))) mapped = TRUE; ++ else handle = dlopen( so_name, RTLD_NOW ); + if (!handle) + { + WARN( "failed to load .so lib %s: %s\n", debugstr_a(so_name), dlerror() ); +@@ -1318,7 +1320,7 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + return STATUS_SUCCESS; + } + +- if (map_so_dll( nt, module )) ++ if (!mapped && map_so_dll( nt, module )) + { + dlclose( handle ); + return STATUS_NO_MEMORY; +From 4fd446bd2e3e289ff792c5315d8291fbcad7aca5 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 May 2022 13:17:32 -0500 +Subject: [PATCH] ntdll: Disable 16-bit TIB hack + +MechWarrior Online (342200) writes directly to the SubSystemTib field, +which Wine would interpret to mean it's a 16-bit executable and then +crash. We're unlikely to run into any real 16-bit applications in Proton +(they won't work on modern Windows, anyway), so let's just disable that +hack entirely. + +CW-Bug-Id: #20673 +--- + dlls/kernelbase/loader.c | 2 +- + dlls/ntdll/env.c | 2 +- + dlls/ntdll/path.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c +index 0fd2d7b7c99..af3c193f331 100644 +--- a/dlls/kernelbase/loader.c ++++ b/dlls/kernelbase/loader.c +@@ -302,7 +302,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameW( HMODULE module, LPWSTR filena + UNICODE_STRING name; + NTSTATUS status; + +- if (!module && ((win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) ++ if (!module && (0 && (win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) + { + len = min( size, win16_tib->exe_name->Length / sizeof(WCHAR) ); + memcpy( filename, win16_tib->exe_name->Buffer, len * sizeof(WCHAR) ); +diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c +index 6db6cee17cd..7d993cd799c 100644 +--- a/dlls/ntdll/env.c ++++ b/dlls/ntdll/env.c +@@ -589,7 +589,7 @@ NTSTATUS WINAPI RtlCreateProcessParametersEx( RTL_USER_PROCESS_PARAMETERS **resu + if (!DllPath) DllPath = &null_str; + if (!CurrentDirectoryName) + { +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = ((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + curdir = cur_params->CurrentDirectory.DosPath; +diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c +index da6f55ddb83..dda6ba4ee53 100644 +--- a/dlls/ntdll/path.c ++++ b/dlls/ntdll/path.c +@@ -528,7 +528,7 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + cd = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -883,7 +883,7 @@ ULONG WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + us = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -927,7 +927,7 @@ NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir; + else + curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory; + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-f5d272f.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-f5d272f.patch new file mode 100644 index 000000000..112db69b5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/legacy/proton-tkg-steamclient-swap-f5d272f.patch @@ -0,0 +1,698 @@ +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index f5a07b510c9e..5c7c2592018c 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -4600,3 +4600,16 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) + if (reason == DLL_PROCESS_ATTACH) LdrDisableThreadCalloutsForDll( inst ); + return TRUE; + } ++ ++ ++const struct unix_funcs *unix_funcs; ++ ++/*********************************************************************** ++ * __wine_set_unix_funcs ++ */ ++NTSTATUS CDECL __wine_set_unix_funcs( int version, const struct unix_funcs *funcs ) ++{ ++ if (version != NTDLL_UNIXLIB_VERSION) return STATUS_REVISION_MISMATCH; ++ unix_funcs = funcs; ++ return STATUS_SUCCESS; ++} +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index 74e37fccbb68..7a5a5afdb6b1 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1692,6 +1692,7 @@ + # Unix interface + @ stdcall __wine_unix_call(int64 long ptr) + @ stdcall __wine_unix_spawnvp(long ptr) ++@ cdecl __wine_set_unix_funcs(long ptr) + @ stdcall __wine_ctrl_routine(ptr) + @ extern -private __wine_syscall_dispatcher + @ extern -private __wine_unix_call_dispatcher +diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h +index 1ce972e3b384..36972a966f1d 100644 +--- a/dlls/ntdll/ntdll_misc.h ++++ b/dlls/ntdll/ntdll_misc.h +@@ -85,6 +85,7 @@ extern const WCHAR windows_dir[] DECLSPEC_HIDDEN; + extern const WCHAR system_dir[] DECLSPEC_HIDDEN; + + extern void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) DECLSPEC_HIDDEN; ++extern const struct unix_funcs *unix_funcs DECLSPEC_HIDDEN; + + extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN; + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 3f16d2f00867..35f2e5f986f7 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -116,6 +116,7 @@ void (WINAPI *pRtlUserThreadStart)( PRTL_THREAD_START_ROUTINE entry, void *a + void (WINAPI *p__wine_ctrl_routine)(void*); + SYSTEM_DLL_INIT_BLOCK *pLdrSystemDllInitBlock = NULL; + ++static NTSTATUS (CDECL *p__wine_set_unix_funcs)( int version, const struct unix_funcs *funcs ); + static void *p__wine_syscall_dispatcher; + + static void * const syscalls[] = +@@ -1062,6 +1061,7 @@ static void load_ntdll_functions( HMODULE module ) + GET_FUNC( LdrSystemDllInitBlock ); + GET_FUNC( RtlUserThreadStart ); + GET_FUNC( __wine_ctrl_routine ); ++ GET_FUNC( __wine_set_unix_funcs ); + GET_FUNC( __wine_syscall_dispatcher ); + #ifdef __i386__ + { +@@ -2191,6 +2182,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = + static void start_main_thread(void) + { + SYSTEM_SERVICE_TABLE syscall_table = { (ULONG_PTR *)syscalls, NULL, ARRAY_SIZE(syscalls), syscall_args }; ++ NTSTATUS status; + TEB *teb = virtual_alloc_first_teb(); + + signal_init_threading(); +@@ -2214,6 +2204,12 @@ static void start_main_thread(void) + if (main_image_info.Machine != current_machine) load_wow64_ntdll( main_image_info.Machine ); + load_apiset_dll(); + ntdll_init_syscalls( 0, &syscall_table, p__wine_syscall_dispatcher ); ++ status = p__wine_set_unix_funcs( NTDLL_UNIXLIB_VERSION, &unix_funcs ); ++ if (status == STATUS_REVISION_MISMATCH) ++ { ++ ERR( "ntdll library version mismatch\n" ); ++ NtTerminateProcess( GetCurrentProcess(), status ); ++ } + server_init_process_done(); + } + +From b9bb042502857bf088bdde3cdb0f998b6fbdcedc Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 13 Jun 2017 12:35:56 -0500 +Subject: [PATCH] HACK: steam: ntdll: Append C:/Program Files (x86)/Steam to + PATH. + +--- + dlls/ntdll/loader.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index bf072af84ac..15d6dde0377 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -84,7 +84,7 @@ const WCHAR system_dir[] = L"C:\\windows\\system32\\"; + HMODULE kernel32_handle = 0; + + /* system search path */ +-static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows"; ++static const WCHAR system_path[] = L"C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\Program Files (x86)\\Steam"; + + static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ + static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ +From 6fa5dfc0bd079bd18e1f457b1e6ae0bcf7eb383d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 5 Nov 2021 23:37:54 +0100 +Subject: [PATCH] HACK: steam: ntdll: Setup steamclient trampolines to + lsteamclient. + +This uses exec page faults to jump from native steamclient into our +lsteamclient entry points. +--- + dlls/ntdll/loader.c | 22 +++++++++++ + dlls/ntdll/unix/loader.c | 67 +++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/signal_i386.c | 7 ++++ + dlls/ntdll/unix/signal_x86_64.c | 7 ++++ + dlls/ntdll/unix/unix_private.h | 3 ++ + dlls/ntdll/unixlib.h | 3 ++ + 6 files changed, 109 insertions(+) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 15d6dde0377..62987663d98 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -2000,12 +2000,16 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + DWORD flags, BOOL system, WINE_MODREF **pwm ) + { + static const char builtin_signature[] = "Wine builtin DLL"; ++ static HMODULE lsteamclient = NULL; + char *signature = (char *)((IMAGE_DOS_HEADER *)*module + 1); ++ UNICODE_STRING lsteamclient_us; + BOOL is_builtin; + IMAGE_NT_HEADERS *nt; + WINE_MODREF *wm; + NTSTATUS status; + SIZE_T map_size; ++ WCHAR *basename, *tmp; ++ ULONG basename_len; + + if (!(nt = RtlImageNtHeader( *module ))) return STATUS_INVALID_IMAGE_FORMAT; + +@@ -2026,6 +2030,24 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, + + set_security_cookie( *module, map_size ); + ++ basename = nt_name->Buffer; ++ if ((tmp = wcsrchr(basename, '\\'))) basename = tmp + 1; ++ if ((tmp = wcsrchr(basename, '/'))) basename = tmp + 1; ++ basename_len = wcslen(basename); ++ if (basename_len >= 4 && !wcscmp(basename + basename_len - 4, L".dll")) basename_len -= 4; ++ ++ if ((!RtlCompareUnicodeStrings(basename, basename_len, L"steamclient", 11, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"steamclient64", 13, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer", 19, TRUE) || ++ !RtlCompareUnicodeStrings(basename, basename_len, L"gameoverlayrenderer64", 21, TRUE)) && ++ RtlCreateUnicodeStringFromAsciiz(&lsteamclient_us, "lsteamclient.dll") && ++ (lsteamclient || LdrLoadDll(load_path, 0, &lsteamclient_us, &lsteamclient) == STATUS_SUCCESS)) ++ { ++ unix_funcs->steamclient_setup_trampolines( *module, lsteamclient ); ++ wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; ++ flags |= DONT_RESOLVE_DLL_REFERENCES; ++ } ++ + /* fixup imports */ + + if (!(flags & DONT_RESOLVE_DLL_REFERENCES) && +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 5dcab3455a0..ecf3cf33947 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2057,6 +2057,72 @@ static ULONG_PTR get_image_address(void) + } + + ++static void *steamclient_srcs[128]; ++static void *steamclient_tgts[128]; ++static int steamclient_count; ++ ++void *steamclient_handle_fault( LPCVOID addr, DWORD err ) ++{ ++ int i; ++ ++ if (!(err & EXCEPTION_EXECUTE_FAULT)) return NULL; ++ ++ for (i = 0; i < steamclient_count; ++i) ++ { ++ if (addr == steamclient_srcs[i]) ++ return steamclient_tgts[i]; ++ } ++ ++ return NULL; ++} ++ ++static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod) ++{ ++ SYSTEM_BASIC_INFORMATION info; ++ IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); ++ IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); ++ IMAGE_SECTION_HEADER *src_sec = (IMAGE_SECTION_HEADER *)(src_nt + 1); ++ const IMAGE_EXPORT_DIRECTORY *src_exp, *tgt_exp; ++ const DWORD *names; ++ SIZE_T size; ++ void *addr, *src_addr, *tgt_addr; ++ char *name; ++ UINT_PTR page_mask; ++ int i; ++ ++ virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); ++ page_mask = info.PageSize - 1; ++ ++ for (i = 0; i < src_nt->FileHeader.NumberOfSections; ++i) ++ { ++ if (memcmp(src_sec[i].Name, ".text", 5)) continue; ++ addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); ++ size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; ++ mprotect(addr, size, PROT_READ); ++ } ++ ++ src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ tgt_exp = get_module_data_dir( tgt_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); ++ names = (const DWORD *)((UINT_PTR)src_mod + src_exp->AddressOfNames); ++ for (i = 0; i < src_exp->NumberOfNames; ++i) ++ { ++ if (!names[i] || !(name = (char *)((UINT_PTR)src_mod + names[i]))) continue; ++ if (!(src_addr = (void *)find_named_export(src_mod, src_exp, name))) continue; ++ if (!(tgt_addr = (void *)find_named_export(tgt_mod, tgt_exp, name))) continue; ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++ } ++ ++ src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); ++ tgt_addr = (void *)((UINT_PTR)tgt_mod + tgt_nt->OptionalHeader.AddressOfEntryPoint); ++ assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); ++ steamclient_srcs[steamclient_count] = src_addr; ++ steamclient_tgts[steamclient_count] = tgt_addr; ++ steamclient_count++; ++} ++ + /*********************************************************************** + * __wine_unix_call_funcs + */ +@@ -2069,6 +2135,15 @@ static struct unix_funcs unix_funcs = + }; + + #endif /* _WIN64 */ ++ ++/*********************************************************************** ++ * unix_funcs ++ */ ++static struct unix_funcs unix_funcs = ++{ ++ steamclient_setup_trampolines, ++}; ++ + + /*********************************************************************** + * start_main_thread +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 6bb5649e2b5..4f471ffb03b 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1887,6 +1887,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + struct xcontext xcontext; + ucontext_t *ucontext = sigcontext; + void *stack = setup_exception_record( sigcontext, &rec, &xcontext ); ++ void *steamclient_addr = NULL; + + switch (TRAP_sig(ucontext)) + { +@@ -1922,6 +1922,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ EIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 68e0c7ce66e..aa5dd47d789 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2182,6 +2182,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + EXCEPTION_RECORD rec = { 0 }; + struct xcontext context; + ucontext_t *ucontext = init_handler( sigcontext ); ++ void *steamclient_addr = NULL; + + rec.ExceptionAddress = (void *)RIP_sig(ucontext); + save_context( &context, ucontext ); +@@ -2213,6 +2213,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) + } + break; + case TRAP_x86_PAGEFLT: /* Page fault */ ++ if ((steamclient_addr = steamclient_handle_fault( siginfo->si_addr, (ERROR_sig(ucontext) >> 1) & 0x09 ))) ++ { ++ RIP_sig(ucontext) = (intptr_t)steamclient_addr; ++ return; ++ } ++ + rec.NumberParameters = 2; + rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; + rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index e736dd3c456..aee0103dd59 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -195,6 +195,9 @@ extern NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct o + + extern void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ) DECLSPEC_HIDDEN; + extern void *anon_mmap_alloc( size_t size, int prot ) DECLSPEC_HIDDEN; ++ ++extern void *steamclient_handle_fault( LPCVOID addr, DWORD err ) DECLSPEC_HIDDEN; ++ + extern void virtual_init(void) DECLSPEC_HIDDEN; + extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; + extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index cb184431f82..8aca8fe31c3 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -40,4 +40,13 @@ struct unix_funcs + + #define NTDLL_UNIX_CALL( func, params ) __wine_unix_call( ntdll_unix_handle, unix_ ## func, params ) + ++/* increment this when you change the function table */ ++#define NTDLL_UNIXLIB_VERSION 139 ++ ++struct unix_funcs ++{ ++ /* steamclient HACK */ ++ void (CDECL *steamclient_setup_trampolines)( HMODULE src_mod, HMODULE tgt_mod ); ++}; ++ + #endif /* __NTDLL_UNIXLIB_H */ +From b25e4e6251675172321a561e1398874fd2dd0126 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 5 Aug 2020 10:35:50 +0200 +Subject: [PATCH] HACK: steam: ntdll: Patch entry points with jumps. + +As a preferred alternative to noexec pages which makes debugging +painful. The noexec can be enabled with WINESTEAMNOEXEC=1 environmnent +variable. +--- + dlls/ntdll/unix/loader.c | 29 +++++++++++++++++++++++++---- + 1 file changed, 25 insertions(+), 4 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ecf3cf33947..5390b8f6779 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2076,8 +2076,23 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) + return NULL; + } + ++static void steamclient_write_jump(void *src_addr, void *tgt_addr) ++{ ++#ifdef _WIN64 ++ static const char mov[] = {0x48, 0xb8}; ++#else ++ static const char mov[] = {0xb8}; ++#endif ++ static const char jmp[] = {0xff, 0xe0}; ++ memcpy(src_addr, mov, sizeof(mov)); ++ memcpy((char *)src_addr + sizeof(mov), &tgt_addr, sizeof(tgt_addr)); ++ memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); ++} ++ + static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod) + { ++ static int noexec_cached = -1; ++ + SYSTEM_BASIC_INFORMATION info; + IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); + IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); +@@ -2086,10 +2101,13 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod + const DWORD *names; + SIZE_T size; + void *addr, *src_addr, *tgt_addr; +- char *name; ++ char *name, *wsne; + UINT_PTR page_mask; + int i; + ++ if (noexec_cached == -1) ++ noexec_cached = (wsne = getenv("WINESTEAMNOEXEC")) && atoi(wsne); ++ + virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); + page_mask = info.PageSize - 1; + +@@ -2098,7 +2116,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod + if (memcmp(src_sec[i].Name, ".text", 5)) continue; + addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); + size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; +- mprotect(addr, size, PROT_READ); ++ if (noexec_cached) mprotect(addr, size, PROT_READ); ++ else mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); + } + + src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); +@@ -2112,7 +2131,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + } + + src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); +@@ -2120,7 +2140,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod + assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; +- steamclient_count++; ++ if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); ++ else steamclient_count++; + } + + /*********************************************************************** +From 901e614e8f3d8913e7f75ccd6cdbabbd0502c53f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 18 Dec 2019 13:49:00 +0100 +Subject: [PATCH] HACK: proton: ntdll: Strip gameoverlayrenderer.so from + LD_PRELOAD before executing explorer.exe. + +Work around a bug in gameoverlayrenderer which introduces 50ms hangs +during XCheckIfEvent after approx 40 minutes of gameplay. + +The original user32 hack broke Steam overlay in Origin games, and Steam +Input consequently. This ntdll implementation should be safer as it'll +modify the environment after the new process has started forking. + +Link: https://github.com/ValveSoftware/Proton/issues/3316 +CW-Bug-Id: #18946 +--- + dlls/ntdll/unix/loader.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index a7e79a828ca..3df74d45bd7 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -725,6 +725,7 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + WORD machine = pe_info->machine; + ULONGLONG res_start = pe_info->base; + ULONGLONG res_end = pe_info->base + pe_info->map_size; ++ const char *ld_preload = getenv( "LD_PRELOAD" ); + char preloader_reserve[64], socket_env[64]; + + if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0; +@@ -759,6 +760,36 @@ NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_i + else loader = is_child_64bit ? "wine64" : "wine"; + } + ++ /* HACK: Unset LD_PRELOAD before executing explorer.exe to disable buggy gameoverlayrenderer.so */ ++ if (ld_preload && argv[2] && !strcmp( argv[2], "C:\\windows\\system32\\explorer.exe" ) && ++ argv[3] && !strcmp( argv[3], "/desktop" )) ++ { ++ static char const gorso[] = "gameoverlayrenderer.so"; ++ static int gorso_len = sizeof(gorso) - 1; ++ int len = strlen( ld_preload ); ++ char *next, *tmp, *env = malloc( sizeof("LD_PRELOAD=") + len ); ++ ++ if (!env) return STATUS_NO_MEMORY; ++ strcpy( env, "LD_PRELOAD=" ); ++ strcat( env, ld_preload ); ++ ++ tmp = env + 11; ++ do ++ { ++ if (!(next = strchr( tmp, ':' ))) next = tmp + strlen( tmp ); ++ if (next - tmp >= gorso_len && strncmp( next - gorso_len, gorso, gorso_len ) == 0) ++ { ++ if (*next) memmove( tmp, next + 1, strlen(next) ); ++ else *tmp = 0; ++ next = tmp; ++ } ++ else tmp = next + 1; ++ } ++ while (*next); ++ ++ putenv( env ); ++ } ++ + signal( SIGPIPE, SIG_DFL ); + + sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); +From bf6233be8f8b7c2e729daa91160dd41fbbb3c64e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 5 Apr 2021 13:20:41 -0500 +Subject: [PATCH] HACK: proton: ntdll: Export a function to set a Unix + environment variable + +--- + dlls/ntdll/env.c | 5 +++++ + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/env.c | 5 +++++ + dlls/ntdll/unix/loader.c | 1 + + dlls/ntdll/unix/unix_private.h | 2 ++ + dlls/ntdll/unixlib.h | 1 + + include/wine/debug.h | 1 + + 7 files changed, 16 insertions(+) + +diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c +index bb8931a556b..0353d6dc501 100644 +--- a/dlls/ntdll/env.c ++++ b/dlls/ntdll/env.c +@@ -684,3 +684,8 @@ void init_user_process_params(void) + set_wow64_environment( &new_params->Environment ); + new_params->EnvironmentSize = RtlSizeHeap( GetProcessHeap(), 0, new_params->Environment ); + } ++ ++void __cdecl __wine_set_unix_env( const char *var, const char *val) ++{ ++ unix_funcs->set_unix_env( var, val ); ++} +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index bd8e1f5efe6..e361fac3a3b 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1636,6 +1636,7 @@ + @ extern __wine_syscall_dispatcher + @ extern -arch=i386 __wine_ldt_copy + @ extern -arch=arm64 __wine_current_teb ++@ cdecl __wine_set_unix_env(ptr ptr) + + # Debugging + @ stdcall -syscall -norelay __wine_dbg_write(ptr long) +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index c7e0674e083..c2c420100d1 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -2616,3 +2616,8 @@ ULONG WINAPI RtlNtStatusToDosError( NTSTATUS status ) + + return map_status( status ); + } ++ ++void CDECL set_unix_env( const char *var, const char *val ) ++{ ++ setenv(var, val, 1); ++} +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 5390b8f6779..a7e79a828ca 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2157,6 +2157,7 @@ static struct unix_funcs unix_funcs = + static struct unix_funcs unix_funcs = + { + steamclient_setup_trampolines, ++ set_unix_env, + }; + + +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index aee0103dd59..25e1445be44 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -290,6 +290,8 @@ extern void call_raise_user_exception_dispatcher(void) DECLSPEC_HIDDEN; + + #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */ + ++extern void CDECL set_unix_env(const char *var, const char *val) DECLSPEC_HIDDEN; ++ + #define TICKSPERSEC 10000000 + #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400) + +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 8aca8fe31c3..595c3931904 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -43,6 +43,7 @@ struct unix_funcs + { + /* steamclient HACK */ + void (CDECL *steamclient_setup_trampolines)( HMODULE src_mod, HMODULE tgt_mod ); ++ void (CDECL *set_unix_env)( const char *var, const char *val ); + }; + + #endif /* __NTDLL_UNIXLIB_H */ +diff --git a/include/wine/debug.h b/include/wine/debug.h +index 6aac7fe82e8..bc2b1ec0c40 100644 +--- a/include/wine/debug.h ++++ b/include/wine/debug.h +@@ -149,6 +149,7 @@ extern const char * __cdecl __wine_dbg_strdup( const char *str ); + extern int __cdecl __wine_dbg_output( const char *str ); + extern int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, + const char *function ); ++extern void __cdecl __wine_set_unix_env( const char *var, const char *val ); + + /* + * Exported definitions and macros + +From a1dde27690950aeb4728f0f3783b4d04d608b5c0 Mon Sep 17 00:00:00 2001 +From: Vincent Povirk +Date: Wed, 1 Apr 2020 11:47:05 -0500 +Subject: [PATCH] winebrowser: Restore original LD_LIBRARY_PATH before calling + to system + +--- + dlls/ntdll/unix/env.c | 3 ++- + programs/winebrowser/main.c | 16 ++++++++++++++++ + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index 45e18dbccaf..6130ad23c15 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -2620,5 +2620,6 @@ ULONG WINAPI RtlNtStatusToDosError( NTSTATUS status ) + + void CDECL set_unix_env( const char *var, const char *val ) + { +- setenv(var, val, 1); ++ if (!val) unsetenv(var); ++ else setenv(var, val, 1); + } +diff --git a/programs/winebrowser/main.c b/programs/winebrowser/main.c +index 913c0a8d672..df3f7425d65 100644 +--- a/programs/winebrowser/main.c ++++ b/programs/winebrowser/main.c +@@ -63,6 +63,17 @@ static char *strdup_unixcp( const WCHAR *str ) + return ret; + } + ++static void restore_system_environment(void) ++{ ++ const char* orig_ld_path = getenv("ORIG_LD_LIBRARY_PATH"); ++ ++ if (orig_ld_path) ++ { ++ __wine_set_unix_env("LD_LIBRARY_PATH", orig_ld_path); ++ __wine_set_unix_env("ORIG_LD_LIBRARY_PATH", NULL); ++ } ++} ++ + /* try to launch a unix app from a comma separated string of app names */ + static int launch_app( const WCHAR *candidates, const WCHAR *argv1 ) + { +@@ -72,6 +83,11 @@ static int launch_app( const WCHAR *candidates, const WCHAR *argv1 ) + + if (!(cmdline = strdup_unixcp( argv1 ))) return 1; + ++ /* PROTON HACK: Restore ORIG_LD_LIBRARY_PATH to LD_LIBRARY_PATH. ++ * System programs may not work correctly with our libraries, in ++ * particular gio on Ubuntu 19.04 is broken by our libgio. */ ++ restore_system_environment(); ++ + while (*candidates) + { + WCHAR **args = CommandLineToArgvW( candidates, &count ); +From a4048df6a5fec29d849bc2b11627d31f4cb01f3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 11 Oct 2021 10:58:33 +0200 +Subject: [PATCH] ntdll: Use RTLD_NOLOAD to find already mapped modules. + +This makes it possible to detect modules that weren't unmapped from +dlclose, and that we should not fixup again. +--- + dlls/ntdll/unix/loader.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index de2270fb678..3790baa9fae 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1258,8 +1258,10 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + { + void *module, *handle; + const IMAGE_NT_HEADERS *nt; ++ BOOL mapped = FALSE; + +- handle = dlopen( so_name, RTLD_NOW ); ++ if ((handle = dlopen( so_name, RTLD_NOW | RTLD_NOLOAD ))) mapped = TRUE; ++ else handle = dlopen( so_name, RTLD_NOW ); + if (!handle) + { + WARN( "failed to load .so lib %s: %s\n", debugstr_a(so_name), dlerror() ); +@@ -1280,7 +1280,7 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * + return STATUS_SUCCESS; + } + +- if (map_so_dll( nt, module )) ++ if (!mapped && map_so_dll( nt, module )) + { + dlclose( handle ); + return STATUS_NO_MEMORY; + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap index 0f98857e7..9d34b4ecb 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap @@ -3,8 +3,14 @@ if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_unfrog" != "true" ]; then source "$_where"/wine-tkg-patches/proton-tkg-specific/proton-staging/staging-winex11-key_translation if [ "$_steamclient_noswap" != "true" ] && git merge-base --is-ancestor b7db0b52cee65a008f503ce727befcad3ba8d28a HEAD; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor a14b4c7d0dbb9a200239070253d6590850654544 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then _patchname='proton-tkg-steamclient-swap.patch' && _patchmsg="Applied steamclient substitution hack" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 7f088b0b1387a3b54c438b839046afadf7948ef5 HEAD ); then + _patchname='proton-tkg-steamclient-swap-59485f0.patch' && _patchmsg="Applied steamclient substitution hack" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f5d272f5610fc0644fced4fe7d54571920a01396 HEAD ); then + _patchname='proton-tkg-steamclient-swap-7f088b0.patch' && _patchmsg="Applied steamclient substitution hack" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor a14b4c7d0dbb9a200239070253d6590850654544 HEAD ); then + _patchname='proton-tkg-steamclient-swap-f5d272f.patch' && _patchmsg="Applied steamclient substitution hack" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor a6bb3662dc484e7809e29308364438ea65cc7637 HEAD ); then _patchname='proton-tkg-steamclient-swap-a14b4c7.patch' && _patchmsg="Applied steamclient substitution hack" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ae8562ed2277a5c051e131dc317e94aa3d5413c8 HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap.patch index 112db69b5..fa9312cd0 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg-steamclient-swap/proton-tkg-steamclient-swap.patch @@ -1,89 +1,3 @@ -diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c -index f5a07b510c9e..5c7c2592018c 100644 ---- a/dlls/ntdll/loader.c -+++ b/dlls/ntdll/loader.c -@@ -4600,3 +4600,16 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) - if (reason == DLL_PROCESS_ATTACH) LdrDisableThreadCalloutsForDll( inst ); - return TRUE; - } -+ -+ -+const struct unix_funcs *unix_funcs; -+ -+/*********************************************************************** -+ * __wine_set_unix_funcs -+ */ -+NTSTATUS CDECL __wine_set_unix_funcs( int version, const struct unix_funcs *funcs ) -+{ -+ if (version != NTDLL_UNIXLIB_VERSION) return STATUS_REVISION_MISMATCH; -+ unix_funcs = funcs; -+ return STATUS_SUCCESS; -+} -diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec -index 74e37fccbb68..7a5a5afdb6b1 100644 ---- a/dlls/ntdll/ntdll.spec -+++ b/dlls/ntdll/ntdll.spec -@@ -1692,6 +1692,7 @@ - # Unix interface - @ stdcall __wine_unix_call(int64 long ptr) - @ stdcall __wine_unix_spawnvp(long ptr) -+@ cdecl __wine_set_unix_funcs(long ptr) - @ stdcall __wine_ctrl_routine(ptr) - @ extern -private __wine_syscall_dispatcher - @ extern -private __wine_unix_call_dispatcher -diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h -index 1ce972e3b384..36972a966f1d 100644 ---- a/dlls/ntdll/ntdll_misc.h -+++ b/dlls/ntdll/ntdll_misc.h -@@ -85,6 +85,7 @@ extern const WCHAR windows_dir[] DECLSPEC_HIDDEN; - extern const WCHAR system_dir[] DECLSPEC_HIDDEN; - - extern void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) DECLSPEC_HIDDEN; -+extern const struct unix_funcs *unix_funcs DECLSPEC_HIDDEN; - - extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN; - -diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index 3f16d2f00867..35f2e5f986f7 100644 ---- a/dlls/ntdll/unix/loader.c -+++ b/dlls/ntdll/unix/loader.c -@@ -116,6 +116,7 @@ void (WINAPI *pRtlUserThreadStart)( PRTL_THREAD_START_ROUTINE entry, void *a - void (WINAPI *p__wine_ctrl_routine)(void*); - SYSTEM_DLL_INIT_BLOCK *pLdrSystemDllInitBlock = NULL; - -+static NTSTATUS (CDECL *p__wine_set_unix_funcs)( int version, const struct unix_funcs *funcs ); - static void *p__wine_syscall_dispatcher; - - static void * const syscalls[] = -@@ -1062,6 +1061,7 @@ static void load_ntdll_functions( HMODULE module ) - GET_FUNC( LdrSystemDllInitBlock ); - GET_FUNC( RtlUserThreadStart ); - GET_FUNC( __wine_ctrl_routine ); -+ GET_FUNC( __wine_set_unix_funcs ); - GET_FUNC( __wine_syscall_dispatcher ); - #ifdef __i386__ - { -@@ -2191,6 +2182,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = - static void start_main_thread(void) - { - SYSTEM_SERVICE_TABLE syscall_table = { (ULONG_PTR *)syscalls, NULL, ARRAY_SIZE(syscalls), syscall_args }; -+ NTSTATUS status; - TEB *teb = virtual_alloc_first_teb(); - - signal_init_threading(); -@@ -2214,6 +2204,12 @@ static void start_main_thread(void) - if (main_image_info.Machine != current_machine) load_wow64_ntdll( main_image_info.Machine ); - load_apiset_dll(); - ntdll_init_syscalls( 0, &syscall_table, p__wine_syscall_dispatcher ); -+ status = p__wine_set_unix_funcs( NTDLL_UNIXLIB_VERSION, &unix_funcs ); -+ if (status == STATUS_REVISION_MISMATCH) -+ { -+ ERR( "ntdll library version mismatch\n" ); -+ NtTerminateProcess( GetCurrentProcess(), status ); -+ } - server_init_process_done(); - } - From b9bb042502857bf088bdde3cdb0f998b6fbdcedc Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Tue, 13 Jun 2017 12:35:56 -0500 @@ -107,28 +21,28 @@ index bf072af84ac..15d6dde0377 100644 static BOOL is_prefix_bootstrap; /* are we bootstrapping the prefix? */ static BOOL imports_fixup_done = FALSE; /* set once the imports have been fixed up, before attaching them */ -From 6fa5dfc0bd079bd18e1f457b1e6ae0bcf7eb383d Mon Sep 17 00:00:00 2001 +From 53ba023e0a3638123c1ae64a070b45b76fdedece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= -Date: Fri, 5 Nov 2021 23:37:54 +0100 +Date: Tue, 22 Nov 2022 11:02:52 +0100 Subject: [PATCH] HACK: steam: ntdll: Setup steamclient trampolines to lsteamclient. This uses exec page faults to jump from native steamclient into our lsteamclient entry points. --- - dlls/ntdll/loader.c | 22 +++++++++++ - dlls/ntdll/unix/loader.c | 67 +++++++++++++++++++++++++++++++++ + dlls/ntdll/loader.c | 23 +++++++++++ + dlls/ntdll/unix/loader.c | 71 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_i386.c | 7 ++++ dlls/ntdll/unix/signal_x86_64.c | 7 ++++ dlls/ntdll/unix/unix_private.h | 3 ++ - dlls/ntdll/unixlib.h | 3 ++ - 6 files changed, 109 insertions(+) + dlls/ntdll/unixlib.h | 7 ++++ + 6 files changed, 118 insertions(+) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c -index 15d6dde0377..62987663d98 100644 +index 81aca02ab40..327979dfe40 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c -@@ -2000,12 +2000,16 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, +@@ -2107,12 +2107,16 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, DWORD flags, BOOL system, WINE_MODREF **pwm ) { static const char builtin_signature[] = "Wine builtin DLL"; @@ -145,7 +59,7 @@ index 15d6dde0377..62987663d98 100644 if (!(nt = RtlImageNtHeader( *module ))) return STATUS_INVALID_IMAGE_FORMAT; -@@ -2026,6 +2030,24 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, +@@ -2133,6 +2137,25 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, set_security_cookie( *module, map_size ); @@ -162,7 +76,8 @@ index 15d6dde0377..62987663d98 100644 + RtlCreateUnicodeStringFromAsciiz(&lsteamclient_us, "lsteamclient.dll") && + (lsteamclient || LdrLoadDll(load_path, 0, &lsteamclient_us, &lsteamclient) == STATUS_SUCCESS)) + { -+ unix_funcs->steamclient_setup_trampolines( *module, lsteamclient ); ++ struct steamclient_setup_trampolines_params params = {.src_mod = *module, .tgt_mod = lsteamclient}; ++ WINE_UNIX_CALL( unix_steamclient_setup_trampolines, ¶ms ); + wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; + flags |= DONT_RESOLVE_DLL_REFERENCES; + } @@ -171,10 +86,10 @@ index 15d6dde0377..62987663d98 100644 if (!(flags & DONT_RESOLVE_DLL_REFERENCES) && diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index 5dcab3455a0..ecf3cf33947 100644 +index a675adea3e3..cf532b236ad 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -2057,6 +2057,72 @@ static ULONG_PTR get_image_address(void) +@@ -1305,6 +1305,81 @@ static NTSTATUS load_so_dll( void *args ) } @@ -197,8 +112,10 @@ index 5dcab3455a0..ecf3cf33947 100644 + return NULL; +} + -+static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod) ++static NTSTATUS steamclient_setup_trampolines( void *args ) +{ ++ struct steamclient_setup_trampolines_params *params = args; ++ HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; + SYSTEM_BASIC_INFORMATION info; + IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); + IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); @@ -242,32 +159,31 @@ index 5dcab3455a0..ecf3cf33947 100644 + steamclient_srcs[steamclient_count] = src_addr; + steamclient_tgts[steamclient_count] = tgt_addr; + steamclient_count++; -+} + - /*********************************************************************** - * __wine_unix_call_funcs - */ -@@ -2069,6 +2135,15 @@ static struct unix_funcs unix_funcs = - }; - - #endif /* _WIN64 */ ++ return STATUS_SUCCESS; ++} + -+/*********************************************************************** -+ * unix_funcs -+ */ -+static struct unix_funcs unix_funcs = ++NTSTATUS unixcall_steamclient_setup_trampolines( void *args ) +{ -+ steamclient_setup_trampolines, -+}; ++ return steamclient_setup_trampolines( args ); ++} + + static const unixlib_entry_t unix_call_funcs[] = + { + load_so_dll, +@@ -1315,6 +1315,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + unixcall_wine_server_handle_to_fd, + unixcall_wine_spawnvp, + system_time_precise, ++ unixcall_steamclient_setup_trampolines, + }; + - /*********************************************************************** - * start_main_thread diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c -index 6bb5649e2b5..4f471ffb03b 100644 +index d800885748f..aa0f10d5bfa 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c -@@ -1887,6 +1887,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +@@ -1859,6 +1859,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) struct xcontext xcontext; ucontext_t *ucontext = sigcontext; void *stack = setup_exception_record( sigcontext, &rec, &xcontext ); @@ -275,7 +191,7 @@ index 6bb5649e2b5..4f471ffb03b 100644 switch (TRAP_sig(ucontext)) { -@@ -1922,6 +1922,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +@@ -1893,6 +1894,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) } break; case TRAP_x86_PAGEFLT: /* Page fault */ @@ -289,10 +205,10 @@ index 6bb5649e2b5..4f471ffb03b 100644 rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c -index 68e0c7ce66e..aa5dd47d789 100644 +index 94d79b9cdd1..b369988b85c 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c -@@ -2182,6 +2182,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +@@ -1966,6 +1966,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) EXCEPTION_RECORD rec = { 0 }; struct xcontext context; ucontext_t *ucontext = init_handler( sigcontext ); @@ -300,7 +216,7 @@ index 68e0c7ce66e..aa5dd47d789 100644 rec.ExceptionAddress = (void *)RIP_sig(ucontext); save_context( &context, ucontext ); -@@ -2213,6 +2213,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +@@ -1997,6 +1998,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) } break; case TRAP_x86_PAGEFLT: /* Page fault */ @@ -314,38 +230,49 @@ index 68e0c7ce66e..aa5dd47d789 100644 rec.ExceptionInformation[0] = (ERROR_sig(ucontext) >> 1) & 0x09; rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index e736dd3c456..aee0103dd59 100644 +index 347d1dd6102..975ac0b334f 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h -@@ -195,6 +195,9 @@ extern NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct o +@@ -197,6 +197,9 @@ extern NTSTATUS system_time_precise( void *args ); - extern void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ) DECLSPEC_HIDDEN; - extern void *anon_mmap_alloc( size_t size, int prot ) DECLSPEC_HIDDEN; + extern void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ); + extern void *anon_mmap_alloc( size_t size, int prot ); + -+extern void *steamclient_handle_fault( LPCVOID addr, DWORD err ) DECLSPEC_HIDDEN; ++extern void *steamclient_handle_fault( LPCVOID addr, DWORD err ); + - extern void virtual_init(void) DECLSPEC_HIDDEN; - extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; - extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ) DECLSPEC_HIDDEN; + extern void virtual_init(void); + extern ULONG_PTR get_system_affinity_mask(void); + extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ); diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h -index cb184431f82..8aca8fe31c3 100644 +index 0b4c2a984bb..36b0ded3bc9 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h -@@ -40,4 +40,13 @@ struct unix_funcs - - #define NTDLL_UNIX_CALL( func, params ) __wine_unix_call( ntdll_unix_handle, unix_ ## func, params ) +@@ -38,17 +38,24 @@ struct unwind_builtin_dll_params + CONTEXT *context; + }; -+/* increment this when you change the function table */ -+#define NTDLL_UNIXLIB_VERSION 139 -+ -+struct unix_funcs ++struct steamclient_setup_trampolines_params +{ -+ /* steamclient HACK */ -+ void (CDECL *steamclient_setup_trampolines)( HMODULE src_mod, HMODULE tgt_mod ); ++ HMODULE src_mod; ++ HMODULE tgt_mod; +}; + - #endif /* __NTDLL_UNIXLIB_H */ -From b25e4e6251675172321a561e1398874fd2dd0126 Mon Sep 17 00:00:00 2001 + enum ntdll_unix_funcs + { + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, + unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, + unix_wine_spawnvp, + unix_system_time_precise, ++ unix_steamclient_setup_trampolines, + }; + + extern unixlib_handle_t __wine_unixlib_handle DECLSPEC_HIDDEN; +From 92e01e33fe6eb993582ba40e931479d78f4e9d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 5 Aug 2020 10:35:50 +0200 Subject: [PATCH] HACK: steam: ntdll: Patch entry points with jumps. @@ -354,14 +281,14 @@ As a preferred alternative to noexec pages which makes debugging painful. The noexec can be enabled with WINESTEAMNOEXEC=1 environmnent variable. --- - dlls/ntdll/unix/loader.c | 29 +++++++++++++++++++++++++---- - 1 file changed, 25 insertions(+), 4 deletions(-) + dlls/ntdll/unix/loader.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index ecf3cf33947..5390b8f6779 100644 +index cf532b236ad..3d250b72178 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -2076,8 +2076,23 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) +@@ -2062,8 +2062,22 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) return NULL; } @@ -378,14 +305,13 @@ index ecf3cf33947..5390b8f6779 100644 + memcpy((char *)src_addr + sizeof(mov) + sizeof(tgt_addr), jmp, sizeof(jmp)); +} + - static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod) + static NTSTATUS steamclient_setup_trampolines( void *args ) { + static int noexec_cached = -1; -+ + struct steamclient_setup_trampolines_params *params = args; + HMODULE src_mod = params->src_mod, tgt_mod = params->tgt_mod; SYSTEM_BASIC_INFORMATION info; - IMAGE_NT_HEADERS *src_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)src_mod + ((IMAGE_DOS_HEADER *)src_mod)->e_lfanew); - IMAGE_NT_HEADERS *tgt_nt = (IMAGE_NT_HEADERS *)((UINT_PTR)tgt_mod + ((IMAGE_DOS_HEADER *)tgt_mod)->e_lfanew); -@@ -2086,10 +2101,13 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod +@@ -2074,10 +2088,13 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) const DWORD *names; SIZE_T size; void *addr, *src_addr, *tgt_addr; @@ -400,7 +326,7 @@ index ecf3cf33947..5390b8f6779 100644 virtual_get_system_info( &info, !!NtCurrentTeb()->WowTebOffset ); page_mask = info.PageSize - 1; -@@ -2098,7 +2116,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod +@@ -2086,7 +2103,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) if (memcmp(src_sec[i].Name, ".text", 5)) continue; addr = (void *)(((UINT_PTR)src_mod + src_sec[i].VirtualAddress) & ~page_mask); size = (src_sec[i].Misc.VirtualSize + page_mask) & ~page_mask; @@ -410,7 +336,7 @@ index ecf3cf33947..5390b8f6779 100644 } src_exp = get_module_data_dir( src_mod, IMAGE_FILE_EXPORT_DIRECTORY, NULL ); -@@ -2112,7 +2131,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod +@@ -2100,7 +2118,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); steamclient_srcs[steamclient_count] = src_addr; steamclient_tgts[steamclient_count] = tgt_addr; @@ -420,16 +346,16 @@ index ecf3cf33947..5390b8f6779 100644 } src_addr = (void *)((UINT_PTR)src_mod + src_nt->OptionalHeader.AddressOfEntryPoint); -@@ -2120,7 +2140,8 @@ static void CDECL steamclient_setup_trampolines(HMODULE src_mod, HMODULE tgt_mod +@@ -2108,7 +2127,8 @@ static NTSTATUS steamclient_setup_trampolines( void *args ) assert(steamclient_count < ARRAY_SIZE(steamclient_srcs)); steamclient_srcs[steamclient_count] = src_addr; steamclient_tgts[steamclient_count] = tgt_addr; - steamclient_count++; + if (!noexec_cached) steamclient_write_jump(src_addr, tgt_addr); + else steamclient_count++; - } - /*********************************************************************** + return STATUS_SUCCESS; + } From 901e614e8f3d8913e7f75ccd6cdbabbd0502c53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 18 Dec 2019 13:49:00 +0100 @@ -498,168 +424,322 @@ index a7e79a828ca..3df74d45bd7 100644 signal( SIGPIPE, SIG_DFL ); sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); -From bf6233be8f8b7c2e729daa91160dd41fbbb3c64e Mon Sep 17 00:00:00 2001 -From: Andrew Eikum -Date: Mon, 5 Apr 2021 13:20:41 -0500 +From 08290ccabc137d205ceec261706d78b021d14fa4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 22 Nov 2022 11:08:22 +0100 Subject: [PATCH] HACK: proton: ntdll: Export a function to set a Unix environment variable --- - dlls/ntdll/env.c | 5 +++++ - dlls/ntdll/ntdll.spec | 1 + - dlls/ntdll/unix/env.c | 5 +++++ - dlls/ntdll/unix/loader.c | 1 + - dlls/ntdll/unix/unix_private.h | 2 ++ - dlls/ntdll/unixlib.h | 1 + - include/wine/debug.h | 1 + - 7 files changed, 16 insertions(+) + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/env.c | 10 +++ + dlls/ntdll/unix/loader.c | 1 + + dlls/ntdll/unix/unix_private.h | 1 + + tools/gdbinit.py | 113 +++++++++++++++++++++++++++++++++ + 5 files changed, 126 insertions(+) + create mode 100644 tools/gdbinit.py -diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c -index bb8931a556b..0353d6dc501 100644 ---- a/dlls/ntdll/env.c -+++ b/dlls/ntdll/env.c -@@ -684,3 +684,8 @@ void init_user_process_params(void) - set_wow64_environment( &new_params->Environment ); - new_params->EnvironmentSize = RtlSizeHeap( GetProcessHeap(), 0, new_params->Environment ); - } -+ -+void __cdecl __wine_set_unix_env( const char *var, const char *val) -+{ -+ unix_funcs->set_unix_env( var, val ); -+} diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec -index bd8e1f5efe6..e361fac3a3b 100644 +index b41e29c0ff5..05999131fc5 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec -@@ -1636,6 +1636,7 @@ - @ extern __wine_syscall_dispatcher - @ extern -arch=i386 __wine_ldt_copy - @ extern -arch=arm64 __wine_current_teb -+@ cdecl __wine_set_unix_env(ptr ptr) +@@ -1696,6 +1696,7 @@ + @ extern -private __wine_syscall_dispatcher + @ extern -private __wine_unix_call_dispatcher + @ extern -private __wine_unixlib_handle ++@ stdcall -syscall __wine_set_unix_env(ptr ptr) # Debugging @ stdcall -syscall -norelay __wine_dbg_write(ptr long) diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c -index c7e0674e083..c2c420100d1 100644 +index 6f1709f4713..3c387ba9027 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c -@@ -2616,3 +2616,8 @@ ULONG WINAPI RtlNtStatusToDosError( NTSTATUS status ) - - return map_status( status ); +@@ -2418,3 +2418,13 @@ void WINAPI RtlSetLastWin32Error( DWORD err ) + #endif + teb->LastErrorValue = err; } + -+void CDECL set_unix_env( const char *var, const char *val ) ++ ++/********************************************************************** ++ * __wine_set_unix_env (ntdll.so) ++ */ ++ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ) +{ + setenv(var, val, 1); ++ return 0; +} -diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index 5390b8f6779..a7e79a828ca 100644 ---- a/dlls/ntdll/unix/loader.c -+++ b/dlls/ntdll/unix/loader.c -@@ -2157,6 +2157,7 @@ static struct unix_funcs unix_funcs = - static struct unix_funcs unix_funcs = - { - steamclient_setup_trampolines, -+ set_unix_env, - }; - - diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index aee0103dd59..25e1445be44 100644 +index 975ac0b334f..a723ae3c5b9 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h -@@ -290,6 +290,8 @@ extern void call_raise_user_exception_dispatcher(void) DECLSPEC_HIDDEN; +@@ -294,6 +294,7 @@ extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, + PNTAPCFUNC func, NTSTATUS status ); + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); + extern void call_raise_user_exception_dispatcher(void); ++extern ULONG WINAPI __wine_set_unix_env( const char *var, const char *val ); #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */ -+extern void CDECL set_unix_env(const char *var, const char *val) DECLSPEC_HIDDEN; +diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c +index 764f5fb2c05..dca651ee770 100644 +--- a/dlls/wow64/syscall.c ++++ b/dlls/wow64/syscall.c +@@ -1335,3 +1335,11 @@ NTSTATUS WINAPI Wow64RaiseException( int code, EXCEPTION_RECORD *rec ) + __ENDTRY + return STATUS_SUCCESS; + } + - #define TICKSPERSEC 10000000 - #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400) - -diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h -index 8aca8fe31c3..595c3931904 100644 ---- a/dlls/ntdll/unixlib.h -+++ b/dlls/ntdll/unixlib.h -@@ -43,6 +43,7 @@ struct unix_funcs - { - /* steamclient HACK */ - void (CDECL *steamclient_setup_trampolines)( HMODULE src_mod, HMODULE tgt_mod ); -+ void (CDECL *set_unix_env)( const char *var, const char *val ); - }; ++/********************************************************************** ++ * wow64___wine_set_unix_env (wow64.@) ++ */ ++NTSTATUS WINAPI wow64___wine_set_unix_env( UINT *args ) ++{ ++ return __wine_set_unix_env(); ++} - #endif /* __NTDLL_UNIXLIB_H */ -diff --git a/include/wine/debug.h b/include/wine/debug.h -index 6aac7fe82e8..bc2b1ec0c40 100644 ---- a/include/wine/debug.h -+++ b/include/wine/debug.h -@@ -149,6 +149,7 @@ extern const char * __cdecl __wine_dbg_strdup( const char *str ); - extern int __cdecl __wine_dbg_output( const char *str ); - extern int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, - const char *function ); -+extern void __cdecl __wine_set_unix_env( const char *var, const char *val ); - - /* - * Exported definitions and macros - -From a1dde27690950aeb4728f0f3783b4d04d608b5c0 Mon Sep 17 00:00:00 2001 -From: Vincent Povirk -Date: Wed, 1 Apr 2020 11:47:05 -0500 -Subject: [PATCH] winebrowser: Restore original LD_LIBRARY_PATH before calling - to system +diff --git a/tools/gdbinit.py b/tools/gdbinit.py +new file mode 100644 +index 00000000000..ba3b7d003ac +--- /dev/null ++++ b/tools/gdbinit.py +@@ -0,0 +1,113 @@ ++#!/bin/env python3 ++ ++# Copyright 2021 RĂ©mi Bernon 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 ++ ++from __future__ import print_function ++ ++import gdb ++import re ++import subprocess ++import sys ++ ++class LoadSymbolFiles(gdb.Command): ++ 'Command to load symbol files directly from /proc//maps.' ++ ++ def __init__(self): ++ sup = super(LoadSymbolFiles, self) ++ sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE, ++ False) ++ ++ self.libs = {} ++ gdb.execute('alias -a lsf = load-symbol-files', True) ++ ++ def invoke(self, arg, from_tty): ++ pid = gdb.selected_inferior().pid ++ if not pid in self.libs: self.libs[pid] = {} ++ ++ def command(cmd, confirm=from_tty, to_string=not from_tty): ++ gdb.execute(cmd, from_tty=confirm, to_string=to_string) ++ ++ def execute(cmd): ++ return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \ ++ .decode('utf-8') ++ ++ # load mappings addresses ++ libs = {} ++ with open('/proc/{}/maps'.format(pid), 'r') as maps: ++ for line in maps: ++ addr, _, _, _, node, path = re.split(r'\s+', line, 5) ++ path = path.strip() ++ if node == '0': continue ++ if path in libs: continue ++ libs[path] = int(addr.split('-')[0], 16) ++ ++ # unload symbol file if address changed ++ for k in set(libs) & set(self.libs[pid]): ++ if libs[k] != self.libs[pid][k]: ++ command('remove-symbol-file "{}"'.format(k), confirm=False) ++ del self.libs[k] ++ ++ # load symbol file for new mappings ++ for k in set(libs) - set(self.libs[pid]): ++ if arg is not None and re.search(arg, k) is None: continue ++ addr = self.libs[pid][k] = libs[k] ++ has_debug = False ++ offs = None ++ ++ try: ++ out = execute(['file', k]) ++ except: ++ continue ++ ++ # try loading mapping as ELF ++ try: ++ out = execute(['readelf', '-l', k]) ++ for line in out.split('\n'): ++ if not 'LOAD' in line: continue ++ base = int(line.split()[2], 16) ++ break ++ except: ++ # assume mapping is PE ++ base = -1 ++ ++ try: ++ name = None ++ cmd = 'add-symbol-file "{}"'.format(k) ++ out = execute(['objdump', '-h', k]) ++ for line in out.split('\n'): ++ if '2**' in line: ++ _, name, _, vma, _, off, _ = line.split(maxsplit=6) ++ if base < 0: offs = int(off, 16) ++ else: offs = int(vma, 16) - base ++ if 'ALLOC' in line: ++ cmd += ' -s {} 0x{:x}'.format(name, addr + offs) ++ elif name in ['.gnu_debuglink', '.debug_info']: ++ has_debug = True ++ elif 'DEBUGGING' in line: ++ has_debug = True ++ except: ++ continue ++ ++ if not has_debug: ++ print('no debugging info found in {}'.format(k)) ++ continue ++ ++ print('loading symbols for {}'.format(k)) ++ command(cmd, confirm=False, to_string=True) ++ ++ ++LoadSymbolFiles() + +From 7aa2679cea3e6edb206da42d23d9d12cb0f1e937 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 21 May 2021 21:54:39 +0200 +Subject: [PATCH] ntdll: Set RLIMIT_NICE to its hard limit and inform the + server. --- - dlls/ntdll/unix/env.c | 3 ++- - programs/winebrowser/main.c | 16 ++++++++++++++++ - 2 files changed, 18 insertions(+), 1 deletion(-) + dlls/ntdll/unix/loader.c | 3 +++ + dlls/ntdll/unix/server.c | 14 ++++++++++++++ + server/process.h | 1 + + server/protocol.def | 1 + + server/thread.c | 7 +++++++ + 5 files changed, 26 insertions(+) -diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c -index 45e18dbccaf..6130ad23c15 100644 ---- a/dlls/ntdll/unix/env.c -+++ b/dlls/ntdll/unix/env.c -@@ -2620,5 +2620,6 @@ ULONG WINAPI RtlNtStatusToDosError( NTSTATUS status ) - - void CDECL set_unix_env( const char *var, const char *val ) - { -- setenv(var, val, 1); -+ if (!val) unsetenv(var); -+ else setenv(var, val, 1); - } -diff --git a/programs/winebrowser/main.c b/programs/winebrowser/main.c -index 913c0a8d672..df3f7425d65 100644 ---- a/programs/winebrowser/main.c -+++ b/programs/winebrowser/main.c -@@ -63,6 +63,17 @@ static char *strdup_unixcp( const WCHAR *str ) - return ret; - } +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index c46f109af71..7ece87338fc 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2548,6 +2548,9 @@ void __wine_main( int argc, char *argv[], char *envp[] ) + #ifdef RLIMIT_AS + set_max_limit( RLIMIT_AS ); + #endif ++#ifdef RLIMIT_NICE ++ set_max_limit( RLIMIT_NICE ); ++#endif -+static void restore_system_environment(void) -+{ -+ const char* orig_ld_path = getenv("ORIG_LD_LIBRARY_PATH"); -+ -+ if (orig_ld_path) + virtual_init(); + init_environment( argc, argv, envp ); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 56a110882ed..321b91d20e0 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -53,6 +53,9 @@ + # include + #endif + #include ++#ifdef HAVE_SYS_RESOURCE_H ++# include ++#endif + #ifdef HAVE_SYS_SYSCALL_H + # include + #endif +@@ -1455,6 +1458,8 @@ size_t server_init_process(void) + struct sigaction sig_act; + size_t info_size; + DWORD pid, tid; ++ struct rlimit rlimit; ++ int nice_limit = 0; + + server_pid = -1; + if (env_socket) +@@ -1516,10 +1521,19 @@ size_t server_init_process(void) + + reply_pipe = init_thread_pipe(); + ++#ifdef RLIMIT_NICE ++ if (!getrlimit( RLIMIT_NICE, &rlimit )) + { -+ __wine_set_unix_env("LD_LIBRARY_PATH", orig_ld_path); -+ __wine_set_unix_env("ORIG_LD_LIBRARY_PATH", NULL); ++ if (rlimit.rlim_cur <= 40) nice_limit = 20 - rlimit.rlim_cur; ++ else if (rlimit.rlim_cur == -1 /* RLIMIT_INFINITY */) nice_limit = -20; + } -+} ++#endif + - /* try to launch a unix app from a comma separated string of app names */ - static int launch_app( const WCHAR *candidates, const WCHAR *argv1 ) - { -@@ -72,6 +83,11 @@ static int launch_app( const WCHAR *candidates, const WCHAR *argv1 ) + SERVER_START_REQ( init_first_thread ) + { + req->unix_pid = getpid(); + req->unix_tid = get_unix_tid(); ++ req->nice_limit = nice_limit; + req->reply_fd = reply_pipe; + req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; + req->debug_level = (TRACE_ON(server) != 0); +diff --git a/server/process.h b/server/process.h +index a0a071d8f88..d073e9e285f 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -50,6 +50,7 @@ struct process + timeout_t sigkill_delay; /* delay before final SIGKILL */ + unsigned short machine; /* client machine type */ + int unix_pid; /* Unix pid for final SIGKILL */ ++ int nice_limit; /* RLIMIT_NICE of the process */ + int exit_code; /* process exit code */ + int running_threads; /* number of threads running in this process */ + timeout_t start_time; /* absolute time at process start */ +diff --git a/server/protocol.def b/server/protocol.def +index 880ad794982..42eae1db9f3 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -941,6 +941,7 @@ typedef struct + int debug_level; /* new debug level */ + int reply_fd; /* fd for reply pipe */ + int wait_fd; /* fd for blocking calls pipe */ ++ char nice_limit; /* RLIMIT_NICE of new thread */ + @REPLY + process_id_t pid; /* process id of the new thread's process */ + thread_id_t tid; /* thread id of the new thread */ +diff --git a/server/thread.c b/server/thread.c +index 2cb45e2bf76..7efa00312bb 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -37,6 +37,12 @@ + #define _WITH_CPU_SET_T + #include + #endif ++#ifdef HAVE_SYS_TIME_H ++#include ++#endif ++#ifdef HAVE_SYS_RESOURCE_H ++#include ++#endif - if (!(cmdline = strdup_unixcp( argv1 ))) return 1; + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -1489,6 +1495,7 @@ DECL_HANDLER(init_first_thread) -+ /* PROTON HACK: Restore ORIG_LD_LIBRARY_PATH to LD_LIBRARY_PATH. -+ * System programs may not work correctly with our libraries, in -+ * particular gio on Ubuntu 19.04 is broken by our libgio. */ -+ restore_system_environment(); -+ - while (*candidates) - { - WCHAR **args = CommandLineToArgvW( candidates, &count ); -From a4048df6a5fec29d849bc2b11627d31f4cb01f3e Mon Sep 17 00:00:00 2001 + current->unix_pid = process->unix_pid = req->unix_pid; + current->unix_tid = req->unix_tid; ++ process->nice_limit = req->nice_limit; + + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); + +From cbf6d8c3fe33c73db63246675e2a0e2672128465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 11 Oct 2021 10:58:33 +0200 Subject: [PATCH] ntdll: Use RTLD_NOLOAD to find already mapped modules. @@ -671,10 +751,10 @@ dlclose, and that we should not fixup again. 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index de2270fb678..3790baa9fae 100644 +index 7ece87338fc..55d36eaec4f 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -1258,8 +1258,10 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * +@@ -1299,8 +1299,10 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * { void *module, *handle; const IMAGE_NT_HEADERS *nt; @@ -686,7 +766,7 @@ index de2270fb678..3790baa9fae 100644 if (!handle) { WARN( "failed to load .so lib %s: %s\n", debugstr_a(so_name), dlerror() ); -@@ -1280,7 +1280,7 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * +@@ -1318,7 +1320,7 @@ static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void * return STATUS_SUCCESS; } @@ -695,4 +775,79 @@ index de2270fb678..3790baa9fae 100644 { dlclose( handle ); return STATUS_NO_MEMORY; +From 4fd446bd2e3e289ff792c5315d8291fbcad7aca5 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 May 2022 13:17:32 -0500 +Subject: [PATCH] ntdll: Disable 16-bit TIB hack + +MechWarrior Online (342200) writes directly to the SubSystemTib field, +which Wine would interpret to mean it's a 16-bit executable and then +crash. We're unlikely to run into any real 16-bit applications in Proton +(they won't work on modern Windows, anyway), so let's just disable that +hack entirely. + +CW-Bug-Id: #20673 +--- + dlls/kernelbase/loader.c | 2 +- + dlls/ntdll/env.c | 2 +- + dlls/ntdll/path.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c +index 0fd2d7b7c99..af3c193f331 100644 +--- a/dlls/kernelbase/loader.c ++++ b/dlls/kernelbase/loader.c +@@ -302,7 +302,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameW( HMODULE module, LPWSTR filena + UNICODE_STRING name; + NTSTATUS status; + +- if (!module && ((win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) ++ if (!module && (0 && (win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) + { + len = min( size, win16_tib->exe_name->Length / sizeof(WCHAR) ); + memcpy( filename, win16_tib->exe_name->Buffer, len * sizeof(WCHAR) ); +diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c +index 6db6cee17cd..7d993cd799c 100644 +--- a/dlls/ntdll/env.c ++++ b/dlls/ntdll/env.c +@@ -589,7 +589,7 @@ NTSTATUS WINAPI RtlCreateProcessParametersEx( RTL_USER_PROCESS_PARAMETERS **resu + if (!DllPath) DllPath = &null_str; + if (!CurrentDirectoryName) + { +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = ((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + curdir = cur_params->CurrentDirectory.DosPath; +diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c +index da6f55ddb83..dda6ba4ee53 100644 +--- a/dlls/ntdll/path.c ++++ b/dlls/ntdll/path.c +@@ -528,7 +528,7 @@ static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + cd = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -883,7 +883,7 @@ ULONG WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + us = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; + else + us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; +@@ -927,7 +927,7 @@ NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir) + + RtlAcquirePebLock(); + +- if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ ++ if (0 && NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ + curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir; + else + curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-161f1b7.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-161f1b7.patch new file mode 100644 index 000000000..4a4db568d --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-161f1b7.patch @@ -0,0 +1,3988 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-2369657.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-2369657.patch new file mode 100644 index 000000000..c0e67489f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-2369657.patch @@ -0,0 +1,3990 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-3681a68.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-3681a68.patch new file mode 100644 index 000000000..18449b760 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-3681a68.patch @@ -0,0 +1,3711 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-48e6bf3.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-48e6bf3.patch new file mode 100644 index 000000000..d4b427b28 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-48e6bf3.patch @@ -0,0 +1,3704 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-643538a.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-643538a.patch new file mode 100644 index 000000000..017bd813f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-643538a.patch @@ -0,0 +1,3615 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7c26c45.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7c26c45.patch new file mode 100644 index 000000000..b84260ea9 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7c26c45.patch @@ -0,0 +1,3704 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7ccb5df.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7ccb5df.patch new file mode 100644 index 000000000..ca20c4b45 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-7ccb5df.patch @@ -0,0 +1,3988 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-828304d.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-828304d.patch new file mode 100644 index 000000000..48503a21e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-828304d.patch @@ -0,0 +1,3988 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-871f333.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-871f333.patch new file mode 100644 index 000000000..238ed3c9a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-871f333.patch @@ -0,0 +1,4027 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 +From: Esme Povirk +Date: Thu, 22 Apr 2021 14:35:40 -0500 +Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter + loads to Bannerlord.exe + +For M&B2:Bannerlord. +--- + dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c +index d9b599fadc7..25acdced21e 100644 +--- a/dlls/mscoree/metahost.c ++++ b/dlls/mscoree/metahost.c +@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname + } + } + ++ if (!strcmp(assemblyname, "ManagedStarter")) ++ { ++ /* HACK for Mount & Blade II: Bannerlord ++ * ++ * The launcher executable uses an AssemblyResolve event handler ++ * to redirect loads of the "ManagedStarter" assembly to ++ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts ++ * to load ManagedStarter before executing the static constructor ++ * that adds this event handler. We work around this by doing the ++ * same thing in our own assembly load hook. */ ++ const char* sgi = getenv("SteamGameId"); ++ if (sgi && !strcmp(sgi, "261550")) ++ { ++ FIXME("hack, using Bannerlord.exe\n"); ++ ++ result = mono_assembly_open("Bannerlord.exe", &stat); ++ ++ if (result) ++ goto done; ++ else ++ { ++ ERR("Bannerlord.exe failed to load\n"); ++ } ++ } ++ } ++ + /* FIXME: We should search the given paths before the GAC. */ + + if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + delete_view( view ); + goto done; + } ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + + /* note: limit is lower than base since the stack grows down */ +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-8c5cf9c.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-8c5cf9c.patch new file mode 100644 index 000000000..b32aa3ca6 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-8c5cf9c.patch @@ -0,0 +1,3829 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-af902c1.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-af902c1.patch new file mode 100644 index 000000000..fcc4780b7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-af902c1.patch @@ -0,0 +1,3979 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + set_page_vprot( (char *)view->base + page_size, page_size, + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD ); + mprotect_range( view->base, 2 * page_size , 0, 0 ); ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + VIRTUAL_DEBUG_DUMP_VIEW( view ); + +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-b4280a4.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-b4280a4.patch new file mode 100644 index 000000000..7fea753a9 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-b4280a4.patch @@ -0,0 +1,3988 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-c14de4c.patch new file mode 100644 index 000000000..4f9201808 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-c14de4c.patch @@ -0,0 +1,3704 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ddc9ef1.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ddc9ef1.patch new file mode 100644 index 000000000..7dbb764f2 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ddc9ef1.patch @@ -0,0 +1,4027 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 +From: Esme Povirk +Date: Thu, 22 Apr 2021 14:35:40 -0500 +Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter + loads to Bannerlord.exe + +For M&B2:Bannerlord. +--- + dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c +index d9b599fadc7..25acdced21e 100644 +--- a/dlls/mscoree/metahost.c ++++ b/dlls/mscoree/metahost.c +@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname + } + } + ++ if (!strcmp(assemblyname, "ManagedStarter")) ++ { ++ /* HACK for Mount & Blade II: Bannerlord ++ * ++ * The launcher executable uses an AssemblyResolve event handler ++ * to redirect loads of the "ManagedStarter" assembly to ++ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts ++ * to load ManagedStarter before executing the static constructor ++ * that adds this event handler. We work around this by doing the ++ * same thing in our own assembly load hook. */ ++ const char* sgi = getenv("SteamGameId"); ++ if (sgi && !strcmp(sgi, "261550")) ++ { ++ FIXME("hack, using Bannerlord.exe\n"); ++ ++ result = mono_assembly_open("Bannerlord.exe", &stat); ++ ++ if (result) ++ goto done; ++ else ++ { ++ ERR("Bannerlord.exe failed to load\n"); ++ } ++ } ++ } ++ + /* FIXME: We should search the given paths before the GAC. */ + + if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + set_page_vprot( (char *)view->base + page_size, page_size, + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD ); + mprotect_range( view->base, 2 * page_size , 0, 0 ); ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + VIRTUAL_DEBUG_DUMP_VIEW( view ); + +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-e900372.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-e900372.patch new file mode 100644 index 000000000..e31cd9964 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-e900372.patch @@ -0,0 +1,3988 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-f2371a8.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-f2371a8.patch new file mode 100644 index 000000000..3a512e0eb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-f2371a8.patch @@ -0,0 +1,3990 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,12 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + const WCHAR *name; + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + if (!attr->RootDirectory) /* without root dir fall back to normal lookup */ ++ { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition ); ++ } + + name = attr->ObjectName->Buffer; + name_len = attr->ObjectName->Length / sizeof(WCHAR); + + if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER; +From cb427c70056654bf12fb0d7d6dcb13386d193ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 24 Apr 2020 14:37:58 +0300 +Subject: [PATCH] server: Try to retrieve the unix name on handles created from + file descriptors. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46070 +Signed-off-by: Gabriel Ivăncescu + +Fixes Unity of Command II, and possibly other games that use +Python38.dll. +--- + server/fd.c | 39 +++++++++++++++++++++++++++++++++++++++ + server/file.c | 1 + + server/file.h | 2 ++ + 3 files changed, 42 insertions(+) + +diff --git a/server/fd.c b/server/fd.c +index 904c99029ca..990a1a708a0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2079,6 +2079,45 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s + return NULL; + } + ++void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ) ++{ ++#ifdef __linux__ ++ static const char procfs_fmt[] = "/proc/self/fd/%d"; ++ ++ char path[PATH_MAX], procfs_path[sizeof(procfs_fmt) - 2 /* %d */ + 11]; ++ struct stat path_st; ++ ssize_t len; ++ ++ sprintf( procfs_path, procfs_fmt, fd->unix_fd ); ++ len = readlink( procfs_path, path, sizeof(path) ); ++ if (len == -1 || len >= sizeof(path) ) ++ return; ++ path[len] = '\0'; ++ ++ /* Make sure it's an absolute path, has at least one hardlink, and the same inode */ ++ if (path[0] != '/' || stat( path, &path_st ) || path_st.st_nlink < 1 || ++ path_st.st_dev != fd_st->st_dev || path_st.st_ino != fd_st->st_ino) ++ return; ++ ++ if (!(fd->unix_name = mem_alloc( len + 1 ))) ++ return; ++ memcpy( fd->unix_name, path, len + 1 ); ++ ++#elif defined(F_GETPATH) ++ char path[PATH_MAX]; ++ size_t size; ++ ++ if (fcntl( fd->unix_fd, F_GETPATH, path ) == -1 || path[0] != '/') ++ return; ++ ++ size = strlen(path) + 1; ++ if (!(fd->unix_name = mem_alloc( size ))) ++ return; ++ memcpy( fd->unix_name, path, size ); ++ ++#endif ++} ++ + /* retrieve the object that is using an fd */ + void *get_fd_user( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index e5e367478ce..e469341c746 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -147,6 +147,7 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari + release_object( file ); + return NULL; + } ++ set_unix_name_of_fd( file->fd, &st ); + allow_fd_caching( file->fd ); + return file; + } +diff --git a/server/file.h b/server/file.h +index 0e43a8170df..7d614569a16 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -22,6 +22,7 @@ + #define __WINE_SERVER_FILE_H + + #include ++#include + + #include "object.h" + +@@ -82,6 +83,7 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t + unsigned int access, unsigned int sharing, unsigned int options ); + extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, + int unix_fd, struct object *user, unsigned int options ); ++extern void set_unix_name_of_fd( struct fd *fd, const struct stat *fd_st ); + extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, + unsigned int options ); + extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing ); + +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + BOOL managed_mode = TRUE; + BOOL decorated_mode = TRUE; + BOOL private_color_map = FALSE; +diff --git a/programs/winecfg/x11drvdlg.c b/programs/winecfg/x11drvdlg.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/x11drvdlg.c ++++ b/programs/winecfg/x11drvdlg.c +@@ -123,7 +123,7 @@ static void init_dialog(HWND dialog) + SendDlgItemMessageW(dialog, IDC_DESKTOP_WIDTH, EM_LIMITTEXT, RES_MAXLEN, 0); + SendDlgItemMessageW(dialog, IDC_DESKTOP_HEIGHT, EM_LIMITTEXT, RES_MAXLEN, 0); + +- buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"N"); ++ buf = get_reg_key(config_key, keypath(L"X11 Driver"), L"GrabFullscreen", L"Y"); + if (IS_OPTION_TRUE(*buf)) + CheckDlgButton(dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED); + else +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/proton-tkg.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/proton-tkg.patch index 238ed3c9a..78aa04770 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/proton-tkg.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/proton-tkg.patch @@ -149,7 +149,7 @@ index c6e3a2ca597..fd919295c5a 100644 - if (!tm) return E_UNEXPECTED; + if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; - actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); + actsvr = malloc(sizeof(ActivatedTextService)); - if (!actsvr) return E_OUTOFMEMORY; + if (!actsvr) goto fail; @@ -160,7 +160,7 @@ index c6e3a2ca597..fd919295c5a 100644 - if (!actsvr->tid) - { -- HeapFree(GetProcessHeap(),0,actsvr); +- free(actsvr); - return E_OUTOFMEMORY; - } + if (!actsvr->tid) goto fail; @@ -174,11 +174,11 @@ index c6e3a2ca597..fd919295c5a 100644 - activate_given_ts(actsvr, tm); + activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); - entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); + entry = malloc(sizeof(AtsEntry)); - - if (!entry) - { -- HeapFree(GetProcessHeap(),0,actsvr); +- free(actsvr); - return E_OUTOFMEMORY; - } + if (!entry) goto fail; @@ -191,7 +191,7 @@ index c6e3a2ca597..fd919295c5a 100644 + +fail: + ITfThreadMgr_Release(tm); -+ HeapFree(GetProcessHeap(), 0, actsvr); ++ free(actsvr); + return E_OUTOFMEMORY; } @@ -237,9 +237,9 @@ index 584bb1044ed..ace2bee23d9 100644 #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 --extern DWORD tlsIndex DECLSPEC_HIDDEN; - extern TfClientId processId DECLSPEC_HIDDEN; - extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c index 2c208fbc04f..2119ea2193b 100644 @@ -376,7 +376,7 @@ index 2c208fbc04f..2119ea2193b 100644 return S_OK; - } - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + This = calloc(1, sizeof(ThreadMgr)); if (This == NULL) @@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; @@ -652,10 +652,10 @@ index 69c4a2f3902..1a3e88a0d29 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE - HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; - HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; - HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; --void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); /* primary.c */ @@ -759,102 +759,6 @@ index 852ec51b7ff..fdbbc00cd33 100644 device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); DSOUND_ParseSpeakerConfig(device); } else if (mixwfe->Format.nChannels > device->num_speakers) { -From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= -Date: Fri, 13 Dec 2019 15:54:28 +0200 -Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Gabriel Ivăncescu ---- - dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- - 1 file changed, 24 insertions(+), 2 deletions(-) - -diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c -index 6378a091f0b..e976fda77f2 100644 ---- a/dlls/dwmapi/dwmapi_main.c -+++ b/dlls/dwmapi/dwmapi_main.c -@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, - */ - HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) - { -- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); -+ if (!hwnd) return E_HANDLE; -+ if (!pv_attribute) return E_INVALIDARG; - -- return E_NOTIMPL; -+ switch (attribute) -+ { -+ case DWMWA_NCRENDERING_ENABLED: -+ if (size < sizeof(BOOL)) return E_INVALIDARG; -+ -+ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); -+ *(BOOL*)(pv_attribute) = FALSE; -+ break; -+ -+ case DWMWA_CLOAKED: -+ if (size < sizeof(DWORD)) return E_INVALIDARG; -+ -+ WARN("DWMWA_CLOAKED: always returning 0.\n"); -+ *(DWORD*)(pv_attribute) = 0; -+ break; -+ -+ default: -+ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); -+ return E_INVALIDARG; -+ } -+ -+ return S_OK; - } - - /********************************************************************** -From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= -Date: Fri, 13 Dec 2019 15:54:30 +0200 -Subject: [PATCH] dwmapi: Add partial implementation of - DWMWA_EXTENDED_FRAME_BOUNDS. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Gabriel Ivăncescu ---- - dlls/dwmapi/Makefile.in | 1 + - dlls/dwmapi/dwmapi_main.c | 7 +++++++ - dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ - 3 files changed, 22 insertions(+) - -diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in -index 3a3691326f8..d273a22c8f3 100644 ---- a/dlls/dwmapi/Makefile.in -+++ b/dlls/dwmapi/Makefile.in -@@ -1,5 +1,6 @@ - MODULE = dwmapi.dll - IMPORTLIB = dwmapi -+IMPORTS = user32 - - EXTRADLLFLAGS = -Wb,--prefer-native - -diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c -index e976fda77f2..212c88c5a02 100644 ---- a/dlls/dwmapi/dwmapi_main.c -+++ b/dlls/dwmapi/dwmapi_main.c -@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib - *(BOOL*)(pv_attribute) = FALSE; - break; - -+ case DWMWA_EXTENDED_FRAME_BOUNDS: -+ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); -+ -+ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); -+ GetWindowRect(hwnd, pv_attribute); -+ break; -+ - case DWMWA_CLOAKED: - if (size < sizeof(DWORD)) return E_INVALIDARG; - From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Mon, 13 Apr 2020 16:25:47 -0700 @@ -1394,7 +1298,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) { - WCHAR *ret = get_bios_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); - if (!ret) return wcsdup( L"The Wine Project" ); + if (!ret) return wcsdup( L"The Proton Project" ); return ret; @@ -1403,7 +1307,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) { - WCHAR *ret = get_bios_string( 2, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1421,7 +1325,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) { - WCHAR *ret = get_compsysproduct_string( 2, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1429,21 +1333,21 @@ index 43268221936..f39d6f0cd7b 100644 static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) { - WCHAR *ret = get_compsysproduct_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); - if (!ret) return wcsdup( L"The Wine Project" ); + if (!ret) return wcsdup( L"The Proton Project" ); return ret; } -@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e rec->index = aa->u.s.IfIndex; rec->interface_index = aa->u.s.IfIndex; rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); - rec->manufacturer = L"The Wine Project"; + rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ rec->name = wcsdup( aa->FriendlyName ); - rec->netconnection_status = get_connection_status( aa->OperStatus ); - rec->physicaladapter = physical; + rec->netenabled = connection_status ? -1 : 0; @@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct rec->lastbootuptime = get_lastbootuptime(); rec->localdatetime = get_localdatetime(); @@ -1456,7 +1360,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) { - WCHAR *ret = get_systemenclosure_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1606,7 +1510,7 @@ index 00000000000..e50ed37f700 + +EXTRADLLFLAGS = -mwindows -mno-cygwin + -+C_SRCS = \ ++SOURCES = \ + main.c diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c new file mode 100644 @@ -1661,9 +1565,9 @@ index af2094a96f0..9c98209d43d 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; BOOL use_system_cursors = TRUE; - BOOL show_systray = TRUE; - BOOL grab_pointer = TRUE; -BOOL grab_fullscreen = FALSE; +BOOL grab_fullscreen = TRUE; BOOL managed_mode = TRUE; @@ -1725,7 +1629,9 @@ index a4a10bc8e93..551ef146cd7 100644 -EXTRADLLFLAGS = -Wb,--prefer-native - - RC_SRCS = version.rc + SOURCES = \ + version.rc \ + vulkan.c From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 25 Mar 2021 17:53:37 +0300 @@ -2088,8 +1994,8 @@ index dca4ffb8647..560e45e965a 100644 + || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) + return ERROR_INVALID_PARAMETER; + -+ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, -+ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); + have_length = *length; + *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 + + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; @@ -2262,6 +2168,68 @@ index be59bc70f5f..c73cb8d26ef 100644 -- 2.30.2 +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 29 Mar 2021 21:54:30 +0300 @@ -2368,7 +2336,7 @@ index af18bdd2c32..020faa26a11 100644 + TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); + + if (!refcount) -+ heap_free(swapchain); ++ free(swapchain); + + return refcount; +} @@ -2817,7 +2785,7 @@ index af18bdd2c32..020faa26a11 100644 + { + struct proxy_swapchain *obj; + -+ obj = heap_alloc_zero(sizeof(*obj)); ++ obj = calloc(1, sizeof(*obj)); + obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; + obj->swapchain = swapchain_impl; + *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; @@ -2912,70 +2880,29 @@ index d3ac9f57763..fcb79983114 100644 + return VK_SUCCESS; +} + - static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + static NTSTATUS WINAPI call_vulkan_debug_report_callback( void *args, ULONG size ) { - return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, + struct wine_vk_debug_report_params *params = args; diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 516c817715f..a0287c1ff09 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan -@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { - - # VK_EXT_calibrated_timestamps - "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, -- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, -+ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, - - # VK_EXT_debug_utils - "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, -From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 -From: Esme Povirk -Date: Thu, 22 Apr 2021 14:35:40 -0500 -Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter - loads to Bannerlord.exe - -For M&B2:Bannerlord. ---- - dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - -diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c -index d9b599fadc7..25acdced21e 100644 ---- a/dlls/mscoree/metahost.c -+++ b/dlls/mscoree/metahost.c -@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname - } - } - -+ if (!strcmp(assemblyname, "ManagedStarter")) -+ { -+ /* HACK for Mount & Blade II: Bannerlord -+ * -+ * The launcher executable uses an AssemblyResolve event handler -+ * to redirect loads of the "ManagedStarter" assembly to -+ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts -+ * to load ManagedStarter before executing the static constructor -+ * that adds this event handler. We work around this by doing the -+ * same thing in our own assembly load hook. */ -+ const char* sgi = getenv("SteamGameId"); -+ if (sgi && !strcmp(sgi, "261550")) -+ { -+ FIXME("hack, using Bannerlord.exe\n"); -+ -+ result = mono_assembly_open("Bannerlord.exe", &stat); -+ -+ if (result) -+ goto done; -+ else -+ { -+ ERR("Bannerlord.exe failed to load\n"); -+ } -+ } -+ } -+ - /* FIXME: We should search the given paths before the GAC. */ - - if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Fri, 7 May 2021 13:44:00 -0500 @@ -3027,9 +2954,9 @@ index acba3544e39..9d1b1cde342 100644 +++ b/dlls/dxgi/dxgi_private.h @@ -209,4 +209,6 @@ struct dxgi_surface HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, - IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + IUnknown *outer, struct wined3d_texture *wined3d_texture); -+HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); + #endif /* __WINE_DXGI_PRIVATE_H */ diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c @@ -3265,11 +3192,11 @@ index c84e0a0a638..69c52616e3f 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -32,6 +32,7 @@ - #include "winbase.h" #undef strncpy + #undef wcsncpy -+extern BOOL erms_supported DECLSPEC_HIDDEN; - extern BOOL sse2_supported DECLSPEC_HIDDEN; ++extern BOOL erms_supported; + extern BOOL sse2_supported; #define DBL80_MAX_10_EXP 4932 diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c @@ -3491,292 +3418,6 @@ index cbc53e22ac1..60b03c57137 100644 } -From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?R=C3=A9mi=20Bernon?= -Date: Fri, 4 Jun 2021 10:24:10 +0200 -Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry - ~Mhz. - -In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor - -Squashed with patches from: - -* Arkadiusz Hiler - -Check if the kernel trusts TSC before using it for Qpc. - -Even if the bits are claiming that TSC meets our requirements the -hardware implementation may still be broken. - -The Linux kernel does a lot of quality testing before deciding to use as -the clock source. If it (or the user, through an override) does not trust -the TSC we should not trust it either. - -* Joshua Ashton - -Some games such as Horizon Zero Dawn use this registry value to -correlate values from rtdsc to real time. - -Testing across a few devices, is seems like Windows always returns the -TSC frequency in this entry, not the current/maximum frequency of the -processor. - -Returning the nominal/maximum cpu frequency here causes the game to run -in slow motion as it may not match the tsc frequency of the processor. - -Ideally we'd not have to measure this and the kernel would return -tsc_khz to userspace, but this is a good enough stop-gap until -https://lkml.org/lkml/2020/12/31/72 or something similar is merged. - -CW-Bug-Id: #18918 -CW-Bug-Id: #18958 ---- - programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- - 1 file changed, 170 insertions(+), 5 deletions(-) - -diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c -index c05ce580298..731242c75eb 100644 ---- a/programs/wineboot/wineboot.c -+++ b/programs/wineboot/wineboot.c -@@ -82,6 +82,8 @@ - - WINE_DEFAULT_DEBUG_CHANNEL(wineboot); - -+#define TICKSPERSEC 10000000 -+ - extern BOOL shutdown_close_windows( BOOL force ); - extern BOOL shutdown_all_desktops( BOOL force ); - extern void kill_processes( BOOL kill_desktop ); -@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) - TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); - } - -+static UINT64 read_tsc_frequency( BOOL has_rdtscp ) -+{ -+ UINT64 freq = 0; -+ -+/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, -+ fix it properly and test it on real Intel hardware */ -+ -+#if 0 -+ int regs[4], cpuid_level, tmp; -+ UINT64 denom, numer; -+ -+ __cpuid( regs, 0 ); -+ tmp = regs[2]; -+ regs[2] = regs[3]; -+ regs[3] = tmp; -+ -+ /* only available on some intel CPUs */ -+ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; -+ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; -+ else -+ { -+ __cpuid( regs, 0x15 ); -+ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; -+ else -+ { -+ if ((freq = regs[2])) freq = freq * numer / denom; -+ else if (cpuid_level >= 0x16) -+ { -+ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ -+ freq = regs[0] * (UINT64)1000000; -+ } -+ else freq = 0; -+ } -+ -+ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); -+ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); -+ } -+#endif -+ -+ if (freq == 0) -+ { -+ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; -+ unsigned int aux; -+ UINT retries = 50; -+ int regs[4]; -+ -+ do -+ { -+ if (has_rdtscp) -+ { -+ tsc0 = __rdtscp( &aux ); -+ time0 = RtlGetSystemTimePrecise(); -+ tsc1 = __rdtscp( &aux ); -+ Sleep( 1 ); -+ tsc2 = __rdtscp( &aux ); -+ time1 = RtlGetSystemTimePrecise(); -+ tsc3 = __rdtscp( &aux ); -+ } -+ else -+ { -+ tsc0 = __rdtsc(); __cpuid( regs, 0 ); -+ time0 = RtlGetSystemTimePrecise(); -+ tsc1 = __rdtsc(); __cpuid( regs, 0 ); -+ Sleep(1); -+ tsc2 = __rdtsc(); __cpuid( regs, 0 ); -+ time1 = RtlGetSystemTimePrecise(); -+ tsc3 = __rdtsc(); __cpuid( regs, 0 ); -+ } -+ -+ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); -+ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); -+ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); -+ } -+ while (error > 100 && --retries); -+ -+ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); -+ else -+ { -+ freq = (freq0 + freq1) / 2; -+ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); -+ } -+ } -+ -+ return freq; -+} -+ -+static BOOL is_tsc_trusted_by_the_kernel(void) -+{ -+ char buf[4] = {}; -+ DWORD num_read; -+ HANDLE handle; -+ BOOL ret = TRUE; -+ -+ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", -+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); -+ if (handle == INVALID_HANDLE_VALUE) return TRUE; -+ -+ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) -+ ret = FALSE; -+ -+ CloseHandle( handle ); -+ return ret; -+} -+ -+static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) -+{ -+ BOOL has_rdtscp = FALSE; -+ int regs[4]; -+ -+ data->QpcBypassEnabled = 0; -+ data->QpcFrequency = TICKSPERSEC; -+ data->QpcShift = 0; -+ data->QpcBias = 0; -+ *tsc_frequency = 0; -+ -+ if (!is_tsc_trusted_by_the_kernel()) -+ { -+ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); -+ return; -+ } -+ -+ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) -+ { -+ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); -+ return; -+ } -+ -+ __cpuid( regs, 0x80000000 ); -+ if (regs[0] < 0x80000007) -+ { -+ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); -+ return; -+ } -+ -+ /* check for invariant tsc bit */ -+ __cpuid( regs, 0x80000007 ); -+ if (!(regs[3] & (1 << 8))) -+ { -+ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); -+ return; -+ } -+ -+ /* check for rdtscp support bit */ -+ __cpuid( regs, 0x80000001 ); -+ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; -+ -+ *tsc_frequency = read_tsc_frequency( has_rdtscp ); -+} -+ - #else - - static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) - { - } - -+static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) -+{ -+ data->QpcBypassEnabled = 0; -+ data->QpcFrequency = TICKSPERSEC; -+ data->QpcShift = 0; -+ data->QpcBias = 0; -+ *tsc_frequency = 0; -+} -+ - #endif - --static void create_user_shared_data(void) -+static void create_user_shared_data( UINT64 *tsc_frequency ) - { - struct _KUSER_SHARED_DATA *data; - RTL_OSVERSIONINFOEXW version; -@@ -368,6 +528,7 @@ static void create_user_shared_data(void) - data->ActiveGroupCount = 1; - - initialize_xstate_features( data ); -+ initialize_qpc_features( data, tsc_frequency ); - - UnmapViewOfFile( data ); - } -@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) - } - - /* create the volatile hardware registry keys */ --static void create_hardware_registry_keys(void) -+static void create_hardware_registry_keys( UINT64 tsc_frequency ) - { - unsigned int i; - HKEY hkey, system_key, cpu_key, fpu_key; -@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) - if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, - KEY_ALL_ACCESS, NULL, &hkey, NULL )) - { -+ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ -+ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; -+ - RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); - set_reg_value( hkey, L"Identifier", id ); - /* TODO: report ARM properly */ - RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, - strlen( (char *)name_buffer ) + 1 ); - set_reg_value( hkey, L"VendorIdentifier", vendorid ); -- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); -+ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); - RegCloseKey( hkey ); - } - if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && -@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) - BOOL end_session, force, init, kill, restart, shutdown, update; - HANDLE event; - OBJECT_ATTRIBUTES attr; -+ UINT64 tsc_frequency = 0; - UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); - BOOL is_wow64; - -@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) - - ResetEvent( event ); /* in case this is a restart */ - -- create_user_shared_data(); -- create_hardware_registry_keys(); -+ create_user_shared_data( &tsc_frequency ); -+ create_hardware_registry_keys( tsc_frequency ); - create_dynamic_registry_keys(); - create_environment_registry_keys(); - create_computer_name_keys(); - From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 5 Jan 2022 13:31:14 +0300 @@ -3827,156 +3468,6 @@ index 00000000000..a1884e53243 +# @ stub DllGetClassObject +# @ stub DllRegisterServer +# @ stub DllUnregisterServer -From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 -From: Paul Gofman -Date: Thu, 10 Feb 2022 16:17:41 +0300 -Subject: [PATCH] ntdll: Guard against syscall stack overrun. - ---- - dlls/ntdll/unix/signal_arm.c | 4 ++++ - dlls/ntdll/unix/signal_arm64.c | 4 ++++ - dlls/ntdll/unix/signal_i386.c | 4 ++++ - dlls/ntdll/unix/signal_x86_64.c | 4 ++++ - dlls/ntdll/unix/unix_private.h | 10 +++++++++- - dlls/ntdll/unix/virtual.c | 5 +++++ - 6 files changed, 30 insertions(+), 1 deletion(-) - -diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c -index eaa41d9e139..ff4dbedccac 100644 ---- a/dlls/ntdll/unix/signal_arm.c -+++ b/dlls/ntdll/unix/signal_arm.c -@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) - (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), - (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c -index 26910173b81..ba3e3564c1a 100644 ---- a/dlls/ntdll/unix/signal_arm64.c -+++ b/dlls/ntdll/unix/signal_arm64.c -@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) - (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), - (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c -index 53da5d3ac59..a0afd33ea13 100644 ---- a/dlls/ntdll/unix/signal_i386.c -+++ b/dlls/ntdll/unix/signal_i386.c -@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, - context->Ebp, context->Esp, context->SegCs, context->SegDs, - context->SegEs, context->SegFs, context->SegGs, context->EFlags ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c -index 7d7f9090986..b1cc682649b 100644 ---- a/dlls/ntdll/unix/signal_x86_64.c -+++ b/dlls/ntdll/unix/signal_x86_64.c -@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, - TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", - context->R12, context->R13, context->R14, context->R15 ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE_(seh)( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index 9bc19e75228..4bf0e7cb84e 100644 ---- a/dlls/ntdll/unix/unix_private.h -+++ b/dlls/ntdll/unix/unix_private.h -@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ - static const SIZE_T signal_stack_mask = 0xffff; - static const SIZE_T signal_stack_size = 0x10000 - 0x3800; - static const SIZE_T kernel_stack_size = 0x100000; --static const SIZE_T min_kernel_stack = 0x2000; -+static const SIZE_T kernel_stack_guard_size = 0x1000; -+static const SIZE_T min_kernel_stack = 0x3000; - static const LONG teb_offset = 0x2000; - - #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) -@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) - (char *)ptr < (char *)get_signal_stack() + signal_stack_size); - } - -+static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) -+{ -+ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; -+ -+ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); -+} -+ - static inline void mutex_lock( pthread_mutex_t *mutex ) - { - if (!process_exiting) pthread_mutex_lock( mutex ); -diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index e7caf162ee1..10e2bf1477c 100644 ---- a/dlls/ntdll/unix/virtual.c -+++ b/dlls/ntdll/unix/virtual.c -@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - SIZE_T commit_size, SIZE_T extra_size ) - { - struct file_view *view; -+ char *kernel_stack; - NTSTATUS status; - sigset_t sigset; - SIZE_T size; -@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - delete_view( view ); - goto done; - } -+ /* setup kernel stack no access guard page */ -+ kernel_stack = (char *)view->base + view->size; -+ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); -+ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); - } - - /* note: limit is lower than base since the stack grows down */ -From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 -From: Paul Gofman -Date: Fri, 16 Sep 2022 17:14:54 -0500 -Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. - -Make guard pages readable. - -CW-Bug-Id: #21305 ---- - dlls/ntdll/unix/virtual.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index 550d7f41dec..8dfe3da0a7e 100644 ---- a/dlls/ntdll/unix/virtual.c -+++ b/dlls/ntdll/unix/virtual.c -@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - } - /* setup kernel stack no access guard page */ - kernel_stack = (char *)view->base + view->size; -- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); -+ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); - mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); - } - From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Thu, 19 Dec 2019 09:12:17 -0600 @@ -4025,3 +3516,100 @@ index 0a080ea6be8..667013df5ac 100644 } pXRRFreeScreenResources( screen_resources ); return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg index 03672d9d9..25c8ad4c2 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg @@ -1,7 +1,7 @@ #!/bin/bash if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_unfrog" != "true" ] && ! git merge-base --is-ancestor 74dc0c5df9c3094352caedda8ebe14ed2dfd615e HEAD || ([ "$_protonify" = "true" ] && git merge-base --is-ancestor 74dc0c5df9c3094352caedda8ebe14ed2dfd615e HEAD); then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9e8fbdab722c9af1feec3f393a6303e69ce91edb HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 643538a836f33b4996730cf05dc2af77667d6150 HEAD ); then if [ "$_use_staging" = "true" ]; then if ! git merge-base --is-ancestor dedd5ccc88547529ffb1101045602aed59fa0170 HEAD; then _patchname='proton-tkg-staging-rpc.patch' && _patchmsg="Using Steam-specific Proton-tkg patches (staging) 1/3" && nonuser_patcher @@ -21,7 +21,75 @@ fi fi else - if git merge-base --is-ancestor 863858da2aa42edfa3b4e639905fcad7056eaeb5 HEAD; then + if git merge-base --is-ancestor 3681a68e7804e0398b3f15f16af4ae19ef2e5dba HEAD; then + _lastcommit="643538a" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 7c26c458588b629be8dbff4e4b22459cf8206108 HEAD; then + _lastcommit="3681a68" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 48e6bf3cf1aabdd2ac640f572416b9169e324f21 HEAD; then + _lastcommit="7c26c45" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD; then + _lastcommit="48e6bf3" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor f2371a8b47cebdc497ec1c4e3d292c0de16df8cd HEAD; then + _lastcommit="c14de4c" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD; then + _lastcommit="f2371a8" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 2369657333cb99d395f6852c07feddb05ee93ae5 HEAD; then + _lastcommit="59485f0" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 7ccb5df0fcfbfdd052fe6032979fb731da2648a9 HEAD; then + _lastcommit="2369657" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 828304dab301a733c7b1a6f53c8bd0681fbae6c4 HEAD; then + _lastcommit="7ccb5df" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 161f1b7b6f0c21d46900176fa3890240805980c4 HEAD; then + _lastcommit="828304d" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor e9003720d6d96e4489452c59c21d967c71bb4338 HEAD; then + _lastcommit="161f1b7" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor b4280a486f7308fd1da260cd5b48b7d9e3c747c5 HEAD; then + _lastcommit="e900372" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 8c5cf9ce16dbedfb2b2e4d3338cf3ea9dc2b1427 HEAD; then + _lastcommit="b4280a4" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor af902c188ebaf7d6dda3b628409d8c390ab410d5 HEAD; then + _lastcommit="8c5cf9c" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor ddc9ef10c9de1ff8885db312f013a069dee21933 HEAD; then + _lastcommit="af902c1" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 871f333d55c2c472014a861bfdd256c1f497dd40 HEAD; then + _lastcommit="ddc9ef1" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 9e8fbdab722c9af1feec3f393a6303e69ce91edb HEAD; then + _lastcommit="871f333" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 863858da2aa42edfa3b4e639905fcad7056eaeb5 HEAD; then _lastcommit="9e8fbda" _rpc="1" _stmbits="1" @@ -439,9 +507,23 @@ fi fi fi - if [ "$_proton_fs_hack" != "true" ] && git merge-base --is-ancestor 3ded60bd1654dc689d24a23305f4a93acce3a6f2 HEAD; then - if git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD; then + if [ "$_proton_fs_hack" != "true" ] && [ "$_wayland_driver" != "true" ] && [ "$_use_staging" = "true" ] && [ "$_childwindow_fix" = "true" ] && git merge-base --is-ancestor 3ded60bd1654dc689d24a23305f4a93acce3a6f2 HEAD; then + if git merge-base --is-ancestor 3c9423618673702ab7a3129f0ca61a0605adab35 HEAD; then _patchname='proton-tkg-additions.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor 2bfe81e41f93ce75139e3a6a2d0b68eb2dcb8fa6 HEAD; then + _patchname='proton-tkg-additions-3c94236.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor 70c9239cb2eef696eea109f3f8e7a58f80cd3823 HEAD; then + _patchname='proton-tkg-additions-2bfe81e.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor 19d2dc0dfc22d0fc1255a462be6494906b9bd3e8 HEAD; then + _patchname='proton-tkg-additions-ad5cb83.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor 6bf3b5539a3be40ed60cada89204685566c141a0 HEAD; then + _patchname='proton-tkg-additions-19d2dc0.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor 1f90d03b7888f902d2aa6101bd60aa47766071cd HEAD; then + _patchname='proton-tkg-additions-6bf3b55.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor af902c188ebaf7d6dda3b628409d8c390ab410d5 HEAD; then + _patchname='proton-tkg-additions-1f90d03.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher + elif git merge-base --is-ancestor ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8 HEAD; then + _patchname='proton-tkg-additions-af902c1.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher elif git merge-base --is-ancestor 6be2138da66db5d1a11787f705a32680c8e59560 HEAD; then _patchname='proton-tkg-additions-ad5cb83.patch' && _patchmsg="Using additional Proton-tkg patches" && nonuser_patcher else diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-19d2dc0.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-19d2dc0.patch new file mode 100644 index 000000000..cbcd6ba05 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-19d2dc0.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (get_capture()) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-1f90d03.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-1f90d03.patch new file mode 100644 index 000000000..299b39510 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-1f90d03.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (event->detail == NotifyPointer) return FALSE; + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + switch (event->mode) + { + case NotifyGrab: +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-2bfe81e.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-2bfe81e.patch new file mode 100644 index 000000000..32e6066d2 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-2bfe81e.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (is_captured_by_system()) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ + BOOL net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-3c94236.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-3c94236.patch new file mode 100644 index 000000000..67afda243 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-3c94236.patch @@ -0,0 +1,455 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (is_captured_by_system()) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ + BOOL net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ + +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-6bf3b55.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-6bf3b55.patch new file mode 100644 index 000000000..6378a238c --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-6bf3b55.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-70c9239.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-70c9239.patch new file mode 100644 index 000000000..51a9553b4 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-70c9239.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/win32u/input.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (is_captured_by_system()) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ ++ } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-af902c1.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-af902c1.patch new file mode 100644 index 000000000..df36c89af --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions-af902c1.patch @@ -0,0 +1,606 @@ +From 19b86fe3d35e00c8985e0c616cd7bc2eeb5e2df7 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 27 Jun 2018 10:06:48 -0500 +Subject: [PATCH] HACK: winex11.drv: Let the WM focus our windows by default. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should fix game windows not being in focus when first started +and when exiting, which can cause surprising keyboard behavior (e.g. +accidentally Alt-F4ing Steam itself, which is in the background). + +This may break modal dialogs in some WMs (fvwm2?) which do not respect +our responses to WM_TAKE_FOCUS. For games that show that issue, we can +re-enable UseTakeFocus. + +From Zeb: + +""" +The basic problem, if I understand it correctly, is that Wine uses the +"globally active" focus model by default. This means that the window +manager won't focus our windows unless we respond in the affirmative to +a WM_TAKE_FOCUS event. Since the GUI thread isn't processing messages, +this doesn't happen. + +Luckily, there is a very easy workaround: create the registry key +HKCU\Software\Wine\X11 Driver and set the somewhat inaptly named value +"UseTakeFocus" to "N" (i.e. No). This causes Wine to use the "locally +active" model instead, which means that the window manager will focus +our windows when it sees fit—i.e. when the user clicks on them, or when +they are created. +""" +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index 8c2688f2a58..6bf4a0a4211 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -68,7 +68,7 @@ BOOL usexrandr = TRUE; + BOOL usexcomposite = TRUE; + BOOL use_xfixes = FALSE; + BOOL use_xpresent = FALSE; +-BOOL use_take_focus = TRUE; ++BOOL use_take_focus = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +From 7563c241f385299b4639c3b939558c2ba430886e Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 18 Sep 2018 14:48:58 -0500 +Subject: [PATCH] winex11.drv: Allow the application to change window size and + states during PropertyNotify. + +On focus loss, fullscreened DDLC changes to a 1x1 pixel window and +minimizes. On restore, it un-minimizes and changes back to fullscreen +size. However, this restoring happens during the PropertyNotify handler, +which means we didn't update size or the NET_WM_STATEs. +--- + dlls/winex11.drv/window.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 01b12f96e03..294d84bfccc 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -2615,7 +2615,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + } + + /* don't change position if we are about to minimize or maximize a managed window */ +- if (!event_type && ++ if ((!event_type || event_type == PropertyNotify) && + !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) + sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + +@@ -2649,7 +2649,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); + } + } + +From 32821e17a97f4e0afb703eaf17280cadb8030a16 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Wed, 17 Oct 2018 19:55:27 +0300 +Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse + after the desktop has been resized. + +This fixes the mouse clipping when the desktop is resized multiple times in a row. +--- + dlls/winex11.drv/mouse.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c +index d02163cbaa8..d2ff071a9f2 100644 +--- a/dlls/winex11.drv/mouse.c ++++ b/dlls/winex11.drv/mouse.c +@@ -579,9 +579,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + release_win_data( data ); + if (!fullscreen) return FALSE; + if (!(thread_data = x11drv_thread_data())) return FALSE; +- if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ +- ++ if (!reset) { ++ if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ ++ } + monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ); + if (!monitor) return FALSE; + monitor_info.cbSize = sizeof(monitor_info); +From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 11 Jan 2019 11:34:03 -0600 +Subject: [PATCH] winex11.drv: Ignore ConfigureNotify messages if there is a + FULLSCREEN WM state pending. + +Into the Breach goes fullscreen by first maximizing, then setting the +window to fullscreen in a separate call. Mutter processes the maximize +request _after_ Wine has sent the fullscreen request. As a result, we +get a ConfigureNotify for the size of the workspace when we expect the +window to be fullscreened. We then notify ITB that it is no longer +fullscreen, which begins the process over again, causing an infinite +loop. + +This fixes that by setting a flag if we have a fullscreen request +pending and ignoring ConfigureNotify requests if it is set. We unset it +when we receive a _NET_WM_STATE PropertyNotify event that contains the +FULLSCREEN flag. +--- + dlls/winex11.drv/event.c | 35 +++++++++++++++++++++++++++++++++++ + dlls/winex11.drv/window.c | 3 +++ + dlls/winex11.drv/x11drv.h | 1 + + 3 files changed, 39 insertions(+) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b7f5614f222..42c66f911ce 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1129,6 +1129,12 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) + event->serial, data->configure_serial ); + goto done; + } ++ if (data->pending_fullscreen) ++ { ++ TRACE( "win %p/%lx event %d,%d,%dx%d pending_fullscreen is pending, so ignoring\n", ++ hwnd, data->whole_window, event->x, event->y, event->width, event->height ); ++ goto done; ++ } + + /* Get geometry */ + +@@ -1362,15 +1368,44 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + } + + ++static void handle__net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) ++{ ++ struct x11drv_win_data *data = get_win_data( hwnd ); ++ ++ if(data->pending_fullscreen) ++ { ++ read_net_wm_states( event->display, data ); ++ if(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)){ ++ data->pending_fullscreen = FALSE; ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen no longer pending.\n", ++ data->net_wm_state); ++ }else ++ TRACE("PropertyNotify _NET_WM_STATE, now 0x%x, pending_fullscreen still pending.\n", ++ data->net_wm_state); ++ } ++ ++ release_win_data( data ); ++} ++ ++ + /*********************************************************************** + * X11DRV_PropertyNotify + */ + static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) + { + XPropertyEvent *event = &xev->xproperty; ++ char *name; + + if (!hwnd) return FALSE; ++ ++ name = XGetAtomName(event->display, event->atom); ++ if(name){ ++ TRACE("win %p PropertyNotify atom: %s, state: 0x%x\n", hwnd, name, event->state); ++ XFree(name); ++ } ++ + if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); ++ else if (event->atom == x11drv_atom(_NET_WM_STATE)) handle__net_wm_state_notify( hwnd, event ); + return TRUE; + } + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 319a28d6fa3..1d42801291c 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1260,6 +1260,7 @@ static void unmap_window( HWND hwnd ) + + data->mapped = FALSE; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + release_win_data( data ); + } +@@ -1276,6 +1277,7 @@ void make_window_embedded( struct x11drv_win_data *data ) + if (!data->managed) XUnmapWindow( data->display, data->whole_window ); + else XWithdrawWindow( data->display, data->whole_window, data->vis.screen ); + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + } + data->embedded = TRUE; + data->managed = TRUE; +@@ -1759,6 +1761,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des + data->whole_colormap = 0; + data->wm_state = WithdrawnState; + data->net_wm_state = 0; ++ data->pending_fullscreen = FALSE; + data->mapped = FALSE; + if (data->xic) + { +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 91cb02b902e..9767fdcf681 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -621,6 +621,7 @@ struct x11drv_win_data + BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ + BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ ++ BOOL pending_fullscreen : 1; + int wm_state; /* current value of the WM_STATE property */ + DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ + Window embedder; /* window id of embedder */ +From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 +From: Arkadiusz Hiler +Date: Tue, 8 Jun 2021 16:01:54 +0300 +Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. + +Full focus lost / focus gained events on the Windows side are not +feasible for X11's FocusIn/FocusOut events generated by keyboard grabs +(see XGrabKeyboard()) that are used for example for Atl+Tab handling. +Using them would degrade user's experience, especially with our full +screen hack, by causing the window to minimize or flash multiple times +depending on a game/window manager combo. + +Because of that the programs may miss on some KEYUP events that happen +during the grab, and since there are no focus changes on the Windows +side the state doesn't get resynced. + +This change attempts to improve user experience by syncing any missed +key release events that happened while the window haven't had focus on +the X11 side. + +There's no syncing of key presses as those are more problematic because +of window manager quirks, e.g. on KDE it may end up syncing the Tab +press portion of Alt+Tab. Luckily missing key events for keys that were +pressed and not released while the WM had the keyboard grab is not +nearly as confusing as stuck keys. + +For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal +and many other games that end up with stuck Alt after Alt+Tabbing. + +CW-Bug-ID: #17046 +CW-Bug-ID: #18904 +--- + dlls/winex11.drv/event.c | 2 ++ + dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- + dlls/winex11.drv/mouse.c | 2 ++ + dlls/winex11.drv/x11drv.h | 1 + + 4 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index b59d4105557..271a70940f1 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) + if (event->detail == NotifyPointer) return FALSE; + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + switch (event->mode) + { + case NotifyGrab: +diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c +index d04d1db9102..720b4869544 100644 +--- a/dlls/winex11.drv/keyboard.c ++++ b/dlls/winex11.drv/keyboard.c +@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + int i, j; + BYTE keystate[256]; + WORD vkey; ++ DWORD flags; ++ KeyCode keycode; ++ HWND keymapnotify_hwnd; + BOOL changed = FALSE; + struct { + WORD vkey; ++ WORD scan; + WORD pressed; + } keys[256]; ++ struct x11drv_thread_data *thread_data = x11drv_thread_data(); ++ ++ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; ++ thread_data->keymapnotify_hwnd = NULL; + + if (!get_async_key_state( keystate )) return FALSE; + +@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) + { + for (j = 0; j < 8; j++) + { +- vkey = keyc2vkey[(i * 8) + j]; ++ keycode = (i * 8) + j; ++ vkey = keyc2vkey[keycode]; + + /* If multiple keys map to the same vkey, we want to report it as + * pressed iff any of them are pressed. */ +- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; ++ if (!keys[vkey & 0xff].vkey) ++ { ++ keys[vkey & 0xff].vkey = vkey; ++ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; ++ } ++ + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + ++ x11drv_thread_data()->keymapnotify_hwnd = hwnd; ++ + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event */ +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 52990283ce7..213143014a7 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -369,6 +369,7 @@ struct x11drv_thread_data + XEvent *current_event; /* event currently being processed */ + HWND grab_hwnd; /* window that currently grabs the mouse */ + HWND last_focus; /* last window that had focus */ ++ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ + XIM xim; /* input method */ + HWND last_xic_hwnd; /* last xic window */ + XFontSet font_set; /* international text drawing font set */ +From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 31 Aug 2021 00:41:15 +0300 +Subject: [PATCH] winex11.drv: HACK: Mind insert_after X11DRV_WindowPosChanged + in some cases. + +Fixes FH4 rendering black window until focus is lost. + +CW-Bug-Id: #19335 +--- + dlls/winex11.drv/window.c | 50 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 4cd2324bff2..815a6279e59 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1481,16 +1481,17 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i + * + * Synchronize the X window position with the Windows one + */ +-static void sync_window_position( struct x11drv_win_data *data, ++static HWND sync_window_position( struct x11drv_win_data *data, + UINT swp_flags, const RECT *old_window_rect, + const RECT *old_whole_rect, const RECT *old_client_rect ) + { + DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); + DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); ++ HWND prev_window = NULL; + XWindowChanges changes; + unsigned int mask = 0; + +- if (data->managed && data->iconic) return; ++ if (data->managed && data->iconic) return NULL; + + /* resizing a managed maximized window is not allowed */ + if (!(style & WS_MAXIMIZE) || !data->managed) +@@ -1529,9 +1530,10 @@ static void sync_window_position( struct x11drv_win_data *data, + { + /* find window that this one must be after */ + HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); ++ + while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) + prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); +- if (!prev) /* top child */ ++ if (!(prev_window = prev)) /* top child */ + { + changes.stack_mode = Above; + mask |= CWStackMode; +@@ -1577,6 +1580,8 @@ static void sync_window_position( struct x11drv_win_data *data, + data->whole_rect.right - data->whole_rect.left, + data->whole_rect.bottom - data->whole_rect.top, + changes.sibling, mask, data->configure_serial ); ++ ++ return prev_window; + } + + +@@ -2733,6 +2738,25 @@ BOOL CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flag + } + + ++static void restack_windows( struct x11drv_win_data *data, HWND prev ) ++{ ++ struct x11drv_win_data *prev_data; ++ ++ TRACE("data->hwnd %p, prev %p.\n", data->hwnd, prev); ++ ++ while (prev) ++ { ++ if (!(prev_data = get_win_data( prev ))) break; ++ ++ TRACE( "Raising window %p.\n", prev ); ++ ++ if (prev_data->whole_window && data->display == prev_data->display) ++ XRaiseWindow( prev_data->display, prev_data->whole_window ); ++ release_win_data( prev_data ); ++ prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); ++ } ++} ++ + /*********************************************************************** + * WindowPosChanged (X11DRV.@) + */ +@@ -2745,6 +2769,7 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + struct x11drv_win_data *data; + DWORD new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + RECT old_window_rect, old_whole_rect, old_client_rect; ++ HWND prev_window = NULL; + int event_type; + + if (!(data = get_win_data( hwnd ))) return; +@@ -2847,8 +2872,8 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + + /* don't change position if we are about to minimize or maximize a managed window */ + if ((!event_type || event_type == PropertyNotify) && +- !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) +- sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); ++ !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) ++ prev_window = sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect ); + + if ((new_style & WS_VISIBLE) && + ((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow ))) +@@ -2864,6 +2889,10 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + release_win_data( data ); + if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); + if (needs_map) map_window( hwnd, new_style ); ++ ++ if (!(data = get_win_data( hwnd ))) return; ++ restack_windows( data, prev_window ); ++ release_win_data( data ); + return; + } + else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE))) +@@ -2880,10 +2909,20 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + else + { + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); +- if (!event_type || event_type == PropertyNotify) update_net_wm_states( data ); ++ if (!event_type || event_type == PropertyNotify) ++ { ++ update_net_wm_states( data ); ++ if (!prev_window && insert_after && data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) ++ { ++ prev_window = NtUserGetWindowRelative( hwnd, GW_HWNDPREV ); ++ if (prev_window != insert_after) prev_window = NULL; ++ } ++ } + } + } + ++ restack_windows( data, prev_window ); ++ + XFlush( data->display ); /* make sure changes are done before we start painting again */ + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +From 399675bd511cbfd57a44e3afaae32a9ddf6dac87 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Wed, 11 Nov 2020 10:41:42 +0800 +Subject: [PATCH] winex11.drv: Call XIconifyWindow() after XMapWindow() with a + minimized window. + +Mutter always unminimizes a window when handling map requests. So a window could be in +normal state as far as Mutter concerns while Wine mistakenly considers it still minimized. + +Fix Disgaea PC black screen after Alt+Tab in fullscreen mode. + +CW-Bug-Id: #18364 +Signed-off-by: Zhiyi Zhang +--- + dlls/winex11.drv/window.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c +index 76d06b57814..b8cba02a0a8 100644 +--- a/dlls/winex11.drv/window.c ++++ b/dlls/winex11.drv/window.c +@@ -1346,6 +1346,9 @@ static void map_window( HWND hwnd, DWORD new_style ) + update_net_wm_states( data ); + sync_window_style( data ); + XMapWindow( data->display, data->whole_window ); ++ /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ ++ if (new_style & WS_MINIMIZE) ++ XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + XFlush( data->display ); + if (data->surface && data->vis.visualid != default_visual.visualid) + data->surface->funcs->flush( data->surface ); +@@ -2947,9 +2950,17 @@ void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags + data->iconic = (new_style & WS_MINIMIZE) != 0; + TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); + if (data->iconic) ++ { + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); ++ } + else if (is_window_rect_mapped( rectWindow )) ++ { ++ /* whole_window could be both iconic and mapped. Since XMapWindow() doesn't do ++ * anything if the window is already mapped, we need to unmap it first */ ++ if (data->mapped) ++ XUnmapWindow( data->display, data->whole_window ); + XMapWindow( data->display, data->whole_window ); ++ } + update_net_wm_states( data ); + } + else +From 1a33ba6c9617af4384601a7a0033fc6a62bb147c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Mon, 7 Mar 2022 16:53:09 +0100 +Subject: [PATCH] winex11.drv: Call SetForegroundWindow instead of + SetActiveWindow on restore. + +So that the window is both active and foreground before we send the +SC_RESTORE command. Project Cars 3 expects that as it tries to reacquire +DInput devices on SC_RESTORE. + +CW-Bug-Id: #19011 +CW-Bug-Id: #20227 +--- + dlls/winex11.drv/event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c +index 7e262646811..15cc47f935a 100644 +--- a/dlls/winex11.drv/event.c ++++ b/dlls/winex11.drv/event.c +@@ -1411,7 +1411,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat + TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window ); + release_win_data( data ); + if ((style & (WS_MINIMIZE | WS_VISIBLE)) == (WS_MINIMIZE | WS_VISIBLE)) +- NtUserSetActiveWindow( hwnd ); ++ NtUserSetForegroundWindow( hwnd ); + send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 ); + return; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions.patch index 8577db511..710e2a055 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/proton-tkg-additions.patch @@ -91,57 +91,27 @@ Subject: [PATCH] winex11.drv: Ignore clip_reset when trying to clip the mouse This fixes the mouse clipping when the desktop is resized multiple times in a row. --- - dlls/winex11.drv/mouse.c | 7 ++++--- + dlls/win32u/input.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) -diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c +diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d02163cbaa8..d2ff071a9f2 100644 ---- a/dlls/winex11.drv/mouse.c -+++ b/dlls/winex11.drv/mouse.c -@@ -579,9 +579,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) - release_win_data( data ); - if (!fullscreen) return FALSE; - if (!(thread_data = x11drv_thread_data())) return FALSE; -- if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; -- if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ +--- a/dlls/win32u/input.c ++++ b/dlls/win32u/input.c +@@ -2485,9 +2485,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (is_captured_by_system()) return FALSE; +- if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; +- if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ - + if (!reset) { -+ if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; -+ if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ ++ if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; ++ if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ + } - monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ); - if (!monitor) return FALSE; - monitor_info.cbSize = sizeof(monitor_info); -From 36535de250f26fbfd46c6383179e4c9bfe2ce956 Mon Sep 17 00:00:00 2001 -From: Alexey Prokhin -Date: Sat, 20 Oct 2018 18:07:12 +0300 -Subject: [PATCH] winex11.drv: Enable fullscreen clipping even if not already - clipping. - ---- - dlls/winex11.drv/mouse.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c -index d2ff071a9f2..9635d264e14 100644 ---- a/dlls/winex11.drv/mouse.c -+++ b/dlls/winex11.drv/mouse.c -@@ -1571,12 +1571,12 @@ BOOL CDECL X11DRV_ClipCursor( LPCRECT clip ) - { - if (grab_clipping_window( clip )) return TRUE; - } -- else /* if currently clipping, check if we should switch to fullscreen clipping */ -+ else /* check if we should switch to fullscreen clipping */ - { - struct x11drv_thread_data *data = x11drv_thread_data(); -- if (data && data->clip_hwnd) -+ if (data) - { -- if (EqualRect( clip, &clip_rect ) || clip_fullscreen_window( foreground, TRUE )) -+ if ((data->clip_hwnd && EqualRect( clip, &clip_rect ) && !EqualRect(&clip_rect, &virtual_rect)) || clip_fullscreen_window( foreground, TRUE )) - return TRUE; - } - } + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) From 24d7f2bda1e98905206476fa3664ea8fa7bd7dd1 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Fri, 11 Jan 2019 11:34:03 -0600 @@ -261,165 +231,14 @@ index 91cb02b902e..9767fdcf681 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -621,6 +621,7 @@ struct x11drv_win_data - BOOL use_alpha : 1; /* does window use an alpha channel? */ - BOOL skip_taskbar : 1; /* does window should be deleted from taskbar */ - BOOL add_taskbar : 1; /* does window should be added to taskbar regardless of style */ -+ BOOL pending_fullscreen : 1; + UINT skip_taskbar : 1; /* does window should be deleted from taskbar */ + UINT add_taskbar : 1; /* does window should be added to taskbar regardless of style */ + UINT net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */ ++ UINT pending_fullscreen : 1; int wm_state; /* current value of the WM_STATE property */ DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ Window embedder; /* window id of embedder */ -From 39000841d68b3d8401bd5602113fdc146fc1ee43 Mon Sep 17 00:00:00 2001 -From: Arkadiusz Hiler -Date: Tue, 8 Jun 2021 16:01:54 +0300 -Subject: [PATCH] winex11.drv: Send missed KEYUP events on KeymapNotify. - -Full focus lost / focus gained events on the Windows side are not -feasible for X11's FocusIn/FocusOut events generated by keyboard grabs -(see XGrabKeyboard()) that are used for example for Atl+Tab handling. -Using them would degrade user's experience, especially with our full -screen hack, by causing the window to minimize or flash multiple times -depending on a game/window manager combo. - -Because of that the programs may miss on some KEYUP events that happen -during the grab, and since there are no focus changes on the Windows -side the state doesn't get resynced. -This change attempts to improve user experience by syncing any missed -key release events that happened while the window haven't had focus on -the X11 side. - -There's no syncing of key presses as those are more problematic because -of window manager quirks, e.g. on KDE it may end up syncing the Tab -press portion of Alt+Tab. Luckily missing key events for keys that were -pressed and not released while the WM had the keyboard grab is not -nearly as confusing as stuck keys. - -For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal -and many other games that end up with stuck Alt after Alt+Tabbing. - -CW-Bug-ID: #17046 -CW-Bug-ID: #18904 ---- - dlls/winex11.drv/event.c | 2 ++ - dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- - dlls/winex11.drv/mouse.c | 2 ++ - dlls/winex11.drv/x11drv.h | 1 + - 4 files changed, 46 insertions(+), 2 deletions(-) - -diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c -index b59d4105557..271a70940f1 100644 ---- a/dlls/winex11.drv/event.c -+++ b/dlls/winex11.drv/event.c -@@ -821,6 +821,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) - if (event->detail == NotifyPointer) return FALSE; - if (hwnd == NtUserGetDesktopWindow()) return FALSE; - -+ x11drv_thread_data()->keymapnotify_hwnd = hwnd; -+ - switch (event->mode) - { - case NotifyGrab: -diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c -index d04d1db9102..720b4869544 100644 ---- a/dlls/winex11.drv/keyboard.c -+++ b/dlls/winex11.drv/keyboard.c -@@ -1209,11 +1209,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) - int i, j; - BYTE keystate[256]; - WORD vkey; -+ DWORD flags; -+ KeyCode keycode; -+ HWND keymapnotify_hwnd; - BOOL changed = FALSE; - struct { - WORD vkey; -+ WORD scan; - WORD pressed; - } keys[256]; -+ struct x11drv_thread_data *thread_data = x11drv_thread_data(); -+ -+ keymapnotify_hwnd = thread_data->keymapnotify_hwnd; -+ thread_data->keymapnotify_hwnd = NULL; - - if (!get_async_key_state( keystate )) return FALSE; - -@@ -1228,11 +1236,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) - { - for (j = 0; j < 8; j++) - { -- vkey = keyc2vkey[(i * 8) + j]; -+ keycode = (i * 8) + j; -+ vkey = keyc2vkey[keycode]; - - /* If multiple keys map to the same vkey, we want to report it as - * pressed iff any of them are pressed. */ -- if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; -+ if (!keys[vkey & 0xff].vkey) -+ { -+ keys[vkey & 0xff].vkey = vkey; -+ keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; -+ } -+ - if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); - -+ x11drv_thread_data()->keymapnotify_hwnd = hwnd; -+ - if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; - - /* simulate a mouse motion event */ -diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h -index 52990283ce7..213143014a7 100644 ---- a/dlls/winex11.drv/x11drv.h -+++ b/dlls/winex11.drv/x11drv.h -@@ -369,6 +369,7 @@ struct x11drv_thread_data - XEvent *current_event; /* event currently being processed */ - HWND grab_hwnd; /* window that currently grabs the mouse */ - HWND last_focus; /* last window that had focus */ -+ HWND keymapnotify_hwnd; /* window that should receive modifier release events */ - XIM xim; /* input method */ - HWND last_xic_hwnd; /* last xic window */ - XFontSet font_set; /* international text drawing font set */ From c5aa20a70a5e681ba718feb7db9adc357cb7bec0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 31 Aug 2021 00:41:15 +0300 diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-161f1b7.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-161f1b7.patch new file mode 100644 index 000000000..d6eb7880a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-161f1b7.patch @@ -0,0 +1,3930 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-2369657.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-2369657.patch new file mode 100644 index 000000000..a102429ea --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-2369657.patch @@ -0,0 +1,3931 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-3681a68.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-3681a68.patch new file mode 100644 index 000000000..144eb229a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-3681a68.patch @@ -0,0 +1,3651 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-48e6bf3.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-48e6bf3.patch new file mode 100644 index 000000000..3440e1d14 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-48e6bf3.patch @@ -0,0 +1,3644 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-59485f0.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-59485f0.patch new file mode 100644 index 000000000..b70cb554f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-59485f0.patch @@ -0,0 +1,3931 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-643538a.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-643538a.patch new file mode 100644 index 000000000..5e6c7cc15 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-643538a.patch @@ -0,0 +1,3555 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7c26c45.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7c26c45.patch new file mode 100644 index 000000000..23e5d99bb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7c26c45.patch @@ -0,0 +1,3644 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture); + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7ccb5df.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7ccb5df.patch new file mode 100644 index 000000000..f65f8b5c0 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-7ccb5df.patch @@ -0,0 +1,3930 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-828304d.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-828304d.patch new file mode 100644 index 000000000..2018c1ede --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-828304d.patch @@ -0,0 +1,3930 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-871f333.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-871f333.patch new file mode 100644 index 000000000..69ff910d5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-871f333.patch @@ -0,0 +1,3969 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 +From: Esme Povirk +Date: Thu, 22 Apr 2021 14:35:40 -0500 +Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter + loads to Bannerlord.exe + +For M&B2:Bannerlord. +--- + dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c +index d9b599fadc7..25acdced21e 100644 +--- a/dlls/mscoree/metahost.c ++++ b/dlls/mscoree/metahost.c +@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname + } + } + ++ if (!strcmp(assemblyname, "ManagedStarter")) ++ { ++ /* HACK for Mount & Blade II: Bannerlord ++ * ++ * The launcher executable uses an AssemblyResolve event handler ++ * to redirect loads of the "ManagedStarter" assembly to ++ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts ++ * to load ManagedStarter before executing the static constructor ++ * that adds this event handler. We work around this by doing the ++ * same thing in our own assembly load hook. */ ++ const char* sgi = getenv("SteamGameId"); ++ if (sgi && !strcmp(sgi, "261550")) ++ { ++ FIXME("hack, using Bannerlord.exe\n"); ++ ++ result = mono_assembly_open("Bannerlord.exe", &stat); ++ ++ if (result) ++ goto done; ++ else ++ { ++ ERR("Bannerlord.exe failed to load\n"); ++ } ++ } ++ } ++ + /* FIXME: We should search the given paths before the GAC. */ + + if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + delete_view( view ); + goto done; + } ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + + /* note: limit is lower than base since the stack grows down */ +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-8c5cf9c.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-8c5cf9c.patch new file mode 100644 index 000000000..18d6c432a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-8c5cf9c.patch @@ -0,0 +1,3771 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-af902c1.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-af902c1.patch new file mode 100644 index 000000000..3d24b2d1a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-af902c1.patch @@ -0,0 +1,3921 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + set_page_vprot( (char *)view->base + page_size, page_size, + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD ); + mprotect_range( view->base, 2 * page_size , 0, 0 ); ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + VIRTUAL_DEBUG_DUMP_VIEW( view ); + +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-b4280a4.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-b4280a4.patch new file mode 100644 index 000000000..f25141948 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-b4280a4.patch @@ -0,0 +1,3930 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-c14de4c.patch new file mode 100644 index 000000000..2af375130 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-c14de4c.patch @@ -0,0 +1,3644 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ddc9ef1.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ddc9ef1.patch new file mode 100644 index 000000000..f4c8f7aa4 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ddc9ef1.patch @@ -0,0 +1,3969 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex DECLSPEC_HIDDEN; + extern TfClientId processId DECLSPEC_HIDDEN; + extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = wcsdup( aa->FriendlyName ); + rec->netconnection_status = get_connection_status( aa->OperStatus ); + rec->physicaladapter = physical; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; + BOOL grab_pointer = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, ++ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 +From: Esme Povirk +Date: Thu, 22 Apr 2021 14:35:40 -0500 +Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter + loads to Bannerlord.exe + +For M&B2:Bannerlord. +--- + dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c +index d9b599fadc7..25acdced21e 100644 +--- a/dlls/mscoree/metahost.c ++++ b/dlls/mscoree/metahost.c +@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname + } + } + ++ if (!strcmp(assemblyname, "ManagedStarter")) ++ { ++ /* HACK for Mount & Blade II: Bannerlord ++ * ++ * The launcher executable uses an AssemblyResolve event handler ++ * to redirect loads of the "ManagedStarter" assembly to ++ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts ++ * to load ManagedStarter before executing the static constructor ++ * that adds this event handler. We work around this by doing the ++ * same thing in our own assembly load hook. */ ++ const char* sgi = getenv("SteamGameId"); ++ if (sgi && !strcmp(sgi, "261550")) ++ { ++ FIXME("hack, using Bannerlord.exe\n"); ++ ++ result = mono_assembly_open("Bannerlord.exe", &stat); ++ ++ if (result) ++ goto done; ++ else ++ { ++ ERR("Bannerlord.exe failed to load\n"); ++ } ++ } ++ } ++ + /* FIXME: We should search the given paths before the GAC. */ + + if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Feb 2022 16:17:41 +0300 +Subject: [PATCH] ntdll: Guard against syscall stack overrun. + +--- + dlls/ntdll/unix/signal_arm.c | 4 ++++ + dlls/ntdll/unix/signal_arm64.c | 4 ++++ + dlls/ntdll/unix/signal_i386.c | 4 ++++ + dlls/ntdll/unix/signal_x86_64.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 10 +++++++++- + dlls/ntdll/unix/virtual.c | 5 +++++ + 6 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c +index eaa41d9e139..ff4dbedccac 100644 +--- a/dlls/ntdll/unix/signal_arm.c ++++ b/dlls/ntdll/unix/signal_arm.c +@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), + (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c +index 26910173b81..ba3e3564c1a 100644 +--- a/dlls/ntdll/unix/signal_arm64.c ++++ b/dlls/ntdll/unix/signal_arm64.c +@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) + (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), + (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index 53da5d3ac59..a0afd33ea13 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, + context->Ebp, context->Esp, context->SegCs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c +index 7d7f9090986..b1cc682649b 100644 +--- a/dlls/ntdll/unix/signal_x86_64.c ++++ b/dlls/ntdll/unix/signal_x86_64.c +@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, + TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", + context->R12, context->R13, context->R14, context->R15 ); + ++ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION ++ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) ++ ERR_(seh)( "Syscall stack overrun.\n "); ++ + if (ntdll_get_thread_data()->jmp_buf) + { + TRACE_(seh)( "returning to handler\n" ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9bc19e75228..4bf0e7cb84e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ + static const SIZE_T signal_stack_mask = 0xffff; + static const SIZE_T signal_stack_size = 0x10000 - 0x3800; + static const SIZE_T kernel_stack_size = 0x100000; +-static const SIZE_T min_kernel_stack = 0x2000; ++static const SIZE_T kernel_stack_guard_size = 0x1000; ++static const SIZE_T min_kernel_stack = 0x3000; + static const LONG teb_offset = 0x2000; + + #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) + (char *)ptr < (char *)get_signal_stack() + signal_stack_size); + } + ++static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) ++{ ++ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; ++ ++ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); ++} ++ + static inline void mutex_lock( pthread_mutex_t *mutex ) + { + if (!process_exiting) pthread_mutex_lock( mutex ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index e7caf162ee1..10e2bf1477c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + SIZE_T commit_size, SIZE_T extra_size ) + { + struct file_view *view; ++ char *kernel_stack; + NTSTATUS status; + sigset_t sigset; + SIZE_T size; +@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + set_page_vprot( (char *)view->base + page_size, page_size, + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD ); + mprotect_range( view->base, 2 * page_size , 0, 0 ); ++ /* setup kernel stack no access guard page */ ++ kernel_stack = (char *)view->base + view->size; ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + VIRTUAL_DEBUG_DUMP_VIEW( view ); + +From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 16 Sep 2022 17:14:54 -0500 +Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. + +Make guard pages readable. + +CW-Bug-Id: #21305 +--- + dlls/ntdll/unix/virtual.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 550d7f41dec..8dfe3da0a7e 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; +- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); ++ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); + } + +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-e900372.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-e900372.patch new file mode 100644 index 000000000..ac6e4b024 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-e900372.patch @@ -0,0 +1,3930 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); +- +- if (!entry) +- { +- HeapFree(GetProcessHeap(),0,actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ HeapFree(GetProcessHeap(), 0, actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_bios_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 2, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_compsysproduct_string( 1, buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_systemenclosure_string( 1, buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++C_SRCS = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + RC_SRCS = version.rc + +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ heap_free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = heap_alloc_zero(sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #include "winbase.h" + #undef strncpy + ++extern BOOL erms_supported DECLSPEC_HIDDEN; + extern BOOL sse2_supported DECLSPEC_HIDDEN; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + + extern BOOL ac_odyssey DECLSPEC_HIDDEN; + extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL simulate_writecopy DECLSPEC_HIDDEN; + + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-f2371a8.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-f2371a8.patch new file mode 100644 index 000000000..4f30d5180 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-f2371a8.patch @@ -0,0 +1,3931 @@ +From aa7fa7ce94bd4e49e3843a8ea398d29882518e43 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 6 Aug 2018 08:06:03 -0500 +Subject: [PATCH] server: Set default timeout to 0 + +The Steam client will be waiting for the wineserver to exit to set up +some environment variables, so make it wait as short as possible. +--- + server/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/main.c b/server/main.c +index 20d3c48c4d9..aca8738c4c0 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -42,7 +42,7 @@ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +-timeout_t master_socket_timeout = 3 * -TICKS_PER_SEC; /* master socket timeout, default is 3 seconds */ ++timeout_t master_socket_timeout = 0; /* master socket timeout, default is 3 seconds */ + const char *server_argv0; + + /* parse-line args */ + +From ba15ba1c9b8ee40e13ef1bd7f018924259f36c1b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 18 Jun 2018 07:56:35 -0500 +Subject: [PATCH] ntdll: Notice THREADNAME_INFO exceptions and set thread name + on Linux + +Patch by Zeb. +--- + dlls/ntdll/unix/thread.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 3b451a22577..ca5dac43bb0 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -43,6 +43,9 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#ifdef HAVE_PRCTL ++#include ++#endif + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -344,6 +347,16 @@ void wait_suspend( CONTEXT *context ) + } + + ++/* "How to: Set a Thread Name in Native Code" ++ * https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ ++typedef struct tagTHREADNAME_INFO ++{ ++ DWORD dwType; /* Must be 0x1000 */ ++ LPCSTR szName; /* Pointer to name - limited to 9 bytes (8 characters + terminator) */ ++ DWORD dwThreadID; /* Thread ID (-1 = caller thread) */ ++ DWORD dwFlags; /* Reserved for future use. Must be zero. */ ++} THREADNAME_INFO; ++ + /********************************************************************** + * send_debug_event + * +@@ -366,6 +379,21 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c + for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) + params[i] = rec->ExceptionInformation[i]; + ++ if (rec->ExceptionCode == 0x406d1388) ++ { ++ const THREADNAME_INFO *threadname = (const THREADNAME_INFO *)rec->ExceptionInformation; ++ ++ if (threadname->dwThreadID == -1) ++ { ++#ifdef HAVE_PRCTL ++#ifndef PR_SET_NAME ++# define PR_SET_NAME 15 ++#endif ++ prctl( PR_SET_NAME, threadname->szName ); ++#endif ++ } ++ } ++ + SERVER_START_REQ( queue_exception_event ) + { + req->first = first_chance; +From 90e3616c89ef7ed38763a3e3af3e9f0cd59697da Mon Sep 17 00:00:00 2001 +From: Nikolay Sivov +Date: Wed, 8 Mar 2017 20:15:40 +0300 +Subject: [PATCH] HACK: dwrite: Don't recommend outline rendering mode + +--- + dlls/dwrite/font.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c +index e22559912c3..41ec809b727 100644 +--- a/dlls/dwrite/font.c ++++ b/dlls/dwrite/font.c +@@ -740,7 +740,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace + + ppem = emSize * ppdip; + +- if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { ++ /* HACK: disable outline rendering mode to workaround d2d issue */ ++ if (0 && ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { + *mode = DWRITE_RENDERING_MODE_OUTLINE; + return S_OK; + } +From 5c59517008697ce74becddb59a3e6702a963bb49 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Fri, 16 Aug 2019 09:46:25 +0000 +Subject: [PATCH] msctf: Use list to keep thread managers. + +Thread managers were stored in thread local storage, +which have a major flaw that they can't not be released +by another thread. + +Signed-off-by: Zhiyi Zhang +--- + dlls/msctf/msctf.c | 46 +++++++---------------- + dlls/msctf/msctf_internal.h | 1 - + dlls/msctf/threadmgr.c | 73 ++++++++++++++++++++++++++++++++----- + 3 files changed, 76 insertions(+), 44 deletions(-) + +diff --git a/dlls/msctf/msctf.c b/dlls/msctf/msctf.c +index c6e3a2ca597..fd919295c5a 100644 +--- a/dlls/msctf/msctf.c ++++ b/dlls/msctf/msctf.c +@@ -69,7 +69,6 @@ static UINT array_size; + static struct list AtsList = LIST_INIT(AtsList); + static UINT activated = 0; + +-DWORD tlsIndex = 0; + TfClientId processId = 0; + ITfCompartmentMgr *globalCompartmentMgr = NULL; + +@@ -397,23 +396,19 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + ActivatedTextService *actsvr; + ITfCategoryMgr *catmgr; + AtsEntry *entry; +- ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ++ ITfThreadMgr *tm; + ITfClientId *clientid; + +- if (!tm) return E_UNEXPECTED; ++ if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; + + actsvr = malloc(sizeof(ActivatedTextService)); +- if (!actsvr) return E_OUTOFMEMORY; ++ if (!actsvr) goto fail; + +- ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ++ ITfThreadMgr_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); + ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); + ITfClientId_Release(clientid); + +- if (!actsvr->tid) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!actsvr->tid) goto fail; + + actsvr->pITfTextInputProcessor = NULL; + actsvr->LanguageProfile = *lp; +@@ -440,20 +435,21 @@ HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) + deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); + + if (activated > 0) +- activate_given_ts(actsvr, tm); ++ activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); + + entry = malloc(sizeof(AtsEntry)); +- +- if (!entry) +- { +- free(actsvr); +- return E_OUTOFMEMORY; +- } ++ if (!entry) goto fail; + + entry->ats = actsvr; + list_add_head(&AtsList, &entry->entry); + ++ ITfThreadMgr_Release(tm); + return S_OK; ++ ++fail: ++ ITfThreadMgr_Release(tm); ++ free(actsvr); ++ return E_OUTOFMEMORY; + } + + BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) +@@ -560,11 +556,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: +- tlsIndex = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (fImpLoad) break; +- TlsFree(tlsIndex); + break; + } + return TRUE; +@@ -622,20 +616,6 @@ HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) + return ThreadMgr_Constructor(NULL,(IUnknown**)pptim); + } + +-/*********************************************************************** +- * TF_GetThreadMgr (MSCTF.@) +- */ +-HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) +-{ +- TRACE("\n"); +- *pptim = TlsGetValue(tlsIndex); +- +- if (*pptim) +- ITfThreadMgr_AddRef(*pptim); +- +- return S_OK; +-} +- + /*********************************************************************** + * SetInputScope(MSCTF.@) + */ +diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h +index 584bb1044ed..ace2bee23d9 100644 +--- a/dlls/msctf/msctf_internal.h ++++ b/dlls/msctf/msctf_internal.h +@@ -35,7 +35,6 @@ + #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 + #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 + +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; + +diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c +index 2c208fbc04f..2119ea2193b 100644 +--- a/dlls/msctf/threadmgr.c ++++ b/dlls/msctf/threadmgr.c +@@ -37,6 +37,17 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(msctf); + ++static CRITICAL_SECTION ThreadMgrCs; ++static CRITICAL_SECTION_DEBUG ThreadMgrCsDebug = ++{ ++ 0, 0, &ThreadMgrCs, ++ {&ThreadMgrCsDebug.ProcessLocksList, ++ &ThreadMgrCsDebug.ProcessLocksList }, ++ 0, 0, {(DWORD_PTR)(__FILE__ ": ThreadMgrCs")} ++}; ++static CRITICAL_SECTION ThreadMgrCs = {&ThreadMgrCsDebug, -1, 0, 0, 0, 0}; ++struct list ThreadMgrList = LIST_INIT(ThreadMgrList); ++ + typedef struct tagPreservedKey + { + struct list entry; +@@ -98,6 +109,9 @@ typedef struct tagACLMulti { + struct list ThreadMgrEventSink; + struct list UIElementSink; + struct list InputProcessorProfileActivationSink; ++ ++ DWORD threadId; ++ struct list entry; + } ThreadMgr; + + typedef struct tagEnumTfDocumentMgr { +@@ -110,6 +124,11 @@ typedef struct tagEnumTfDocumentMgr { + + static HRESULT EnumTfDocumentMgr_Constructor(struct list* head, IEnumTfDocumentMgrs **ppOut); + ++static inline ThreadMgr *impl_from_ITfThreadMgr(ITfThreadMgr *iface) ++{ ++ return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); ++} ++ + static inline ThreadMgr *impl_from_ITfThreadMgrEx(ITfThreadMgrEx *iface) + { + return CONTAINING_RECORD(iface, ThreadMgr, ITfThreadMgrEx_iface); +@@ -155,6 +174,35 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg + return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); + } + ++/*********************************************************************** ++ * TF_GetThreadMgr (MSCTF.@) ++ */ ++HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) ++{ ++ DWORD id = GetCurrentThreadId(); ++ ThreadMgr *cursor; ++ ++ TRACE("%p\n", pptim); ++ ++ if (!pptim) ++ return E_INVALIDARG; ++ ++ EnterCriticalSection(&ThreadMgrCs); ++ LIST_FOR_EACH_ENTRY(cursor, &ThreadMgrList, ThreadMgr, entry) ++ { ++ if (cursor->threadId == id) ++ { ++ ITfThreadMgrEx_AddRef(&cursor->ITfThreadMgrEx_iface); ++ *pptim = (ITfThreadMgr *)&cursor->ITfThreadMgrEx_iface; ++ LeaveCriticalSection(&ThreadMgrCs); ++ return S_OK; ++ } ++ } ++ LeaveCriticalSection(&ThreadMgrCs); ++ *pptim = NULL; ++ return E_FAIL; ++} ++ + static void ThreadMgr_Destructor(ThreadMgr *This) + { + struct list *cursor, *cursor2; +@@ -163,7 +211,9 @@ static void ThreadMgr_Destructor(ThreadMgr *This) + if (This->focusHook) + NtUserUnhookWindowsHookEx(This->focusHook); + +- TlsSetValue(tlsIndex,NULL); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_remove(&This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); + TRACE("destroying %p\n", This); + if (This->focus) + ITfDocumentMgr_Release(This->focus); +@@ -386,17 +436,20 @@ static HRESULT WINAPI ThreadMgr_SetFocus(ITfThreadMgrEx *iface, ITfDocumentMgr * + + static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lParam) + { ++ ITfThreadMgr *ThreadMgr_iface; + ThreadMgr *This; + +- This = TlsGetValue(tlsIndex); +- if (!This) ++ if (FAILED(TF_GetThreadMgr(&ThreadMgr_iface))) + { + ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); + return 0; + } ++ ++ This = impl_from_ITfThreadMgr(ThreadMgr_iface); + if (!This->focusHook) + { + ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); ++ ITfThreadMgr_Release(ThreadMgr_iface); + return 0; + } + +@@ -417,6 +470,7 @@ static LRESULT CALLBACK ThreadFocusHookProc(int nCode, WPARAM wParam, LPARAM lPa + } + } + ++ ITfThreadMgr_Release(ThreadMgr_iface); + return CallNextHookEx(This->focusHook, nCode, wParam, lParam); + } + +@@ -1338,13 +1392,8 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + return CLASS_E_NOAGGREGATION; + + /* Only 1 ThreadMgr is created per thread */ +- This = TlsGetValue(tlsIndex); +- if (This) +- { +- ThreadMgr_AddRef(&This->ITfThreadMgrEx_iface); +- *ppOut = (IUnknown*)&This->ITfThreadMgrEx_iface; ++ if (SUCCEEDED(TF_GetThreadMgr((ITfThreadMgr **)ppOut))) + return S_OK; +- } + + This = calloc(1, sizeof(ThreadMgr)); + if (This == NULL) +@@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; + This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->refCount = 1; +- TlsSetValue(tlsIndex,This); + + CompartmentMgr_Constructor((IUnknown*)&This->ITfThreadMgrEx_iface, &IID_IUnknown, (IUnknown**)&This->CompartmentMgr); + +@@ -1376,6 +1424,11 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) + list_init(&This->UIElementSink); + list_init(&This->InputProcessorProfileActivationSink); + ++ This->threadId = GetCurrentThreadId(); ++ EnterCriticalSection(&ThreadMgrCs); ++ list_add_tail(&ThreadMgrList, &This->entry); ++ LeaveCriticalSection(&ThreadMgrCs); ++ + TRACE("returning %p\n", This); + *ppOut = (IUnknown *)&This->ITfThreadMgrEx_iface; + return S_OK; +From ed04e35d3f7af02267fb4e21578b3ccb27703836 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Mon, 23 Sep 2019 13:29:16 -0500 +Subject: [PATCH] dxdiag: Dump to stdout if no filename is given + +--- + programs/dxdiag/main.c | 8 +++++++- + programs/dxdiag/output.c | 10 +++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c +index 4533236f0f5..353e9f50fb9 100644 +--- a/programs/dxdiag/main.c ++++ b/programs/dxdiag/main.c +@@ -71,7 +71,13 @@ static BOOL process_file_name(const WCHAR *cmdline, enum output_type output_type + endptr = cmdline + lstrlenW(cmdline); + + len = endptr - cmdline; +- if (len == 0 || len >= filename_len) ++ if (len == 0) ++ { ++ *filename = 0; ++ return TRUE; ++ } ++ ++ if (len >= filename_len) + return FALSE; + + memcpy(filename, cmdline, len * sizeof(WCHAR)); +diff --git a/programs/dxdiag/output.c b/programs/dxdiag/output.c +index 50240fb2860..f0f6a6da0c3 100644 +--- a/programs/dxdiag/output.c ++++ b/programs/dxdiag/output.c +@@ -169,8 +169,12 @@ static BOOL output_text_information(struct dxdiag_information *dxdiag_info, cons + + fill_system_text_output_table(dxdiag_info, output_table[0].fields); + +- hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, +- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (filename && *filename) ++ hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ++ else ++ hFile = GetStdHandle(STD_OUTPUT_HANDLE); ++ + if (hFile == INVALID_HANDLE_VALUE) + { + WINE_ERR("File creation failed, last error %u\n", GetLastError()); +@@ -227,7 +231,7 @@ static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename) + VARIANT destVar; + HRESULT hr; + +- if (!bstr) ++ if (!bstr || !filename || !*filename) + return E_OUTOFMEMORY; + + V_VT(&destVar) = VT_BSTR; +From 5cd65deffffad9073538acf4fd8e794ac07824a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 9 Oct 2019 09:47:12 +0200 +Subject: [PATCH] makedep: Align PE sections so they can be directly mmaped. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should help linux perf tool match the binary files on disk with the +code regions in memory. + +Signed-off-by: RĂ©mi Bernon +--- + tools/makedep.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/makedep.c b/tools/makedep.c +index 6079d1fe28b..18e7b8a913e 100644 +--- a/tools/makedep.c ++++ b/tools/makedep.c +@@ -3245,6 +3245,7 @@ static void output_module( struct makefile *make ) + if (debug_file) output_filename( strmake( "-Wl,--debug-file,%s", obj_dir_path( make, debug_file ))); + output_filenames( all_libs ); + output_filename( arch_make_variable( "LDFLAGS", arch )); ++ output_filename( "-Wl,--file-alignment,4096" ); + output( "\n" ); + + if (*dll_ext && make->is_exe && !make->is_win16 && strendswith( make->module, ".exe" )) +From 213905a322620eb326b655ab89fbca07316e6357 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 19 Nov 2019 09:59:17 -0600 +Subject: [PATCH] HACK: dxgi: Return empty GPU string for Crazy Machines 3 + +If the GPU string is long enough, the game will crash trying to +dereference part of it. Probably this is due to missing Media Foundation +support. Try to remove this hack after the game's videos successfully +play back. +--- + dlls/dxgi/adapter.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index d2fc629c843..5a97590dbce 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -161,12 +161,32 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + if (FAILED(hr = wined3d_get_adapter_identifier(adapter->factory->wined3d, adapter->ordinal, 0, &adapter_id))) + return hr; + ++ { ++ /* HACK for Proton issue #3204 ++ * ++ * Due to reading uninitialized memory, the game tries to dereference ++ * part of the GPU Description string if it is long enough. So return ++ * an empty string instead. ++ * ++ * See the bug report for the full description, but we may be able to ++ * remove this hack after implementing enough of Media Foundation for ++ * this game's videos to play back. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "351920")) ++ { ++ desc->Description[0] = 0; ++ goto skip_description; ++ } ++ } ++ + if (!MultiByteToWideChar(CP_ACP, 0, description, -1, desc->Description, ARRAY_SIZE(description))) + { + DWORD err = GetLastError(); + ERR("Failed to translate description %s (%#lx).\n", debugstr_a(description), err); + hr = E_FAIL; + } ++skip_description: + + desc->VendorId = adapter_id.vendor_id; + desc->DeviceId = adapter_id.device_id; +From 4aa052e0c8ae276fc07afcd93d6e290a88214837 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jan 2020 10:17:23 -0600 +Subject: [PATCH] dsound: Initialize primary buffer with device's channel + layout + +Fixes surround sound in some games, like Borderlands GOTY and Dead +Space. +--- + dlls/dsound/dsound.c | 77 +----------------------------------- + dlls/dsound/dsound_private.h | 1 - + dlls/dsound/primary.c | 75 ++++++++++++++++++++++++++++++++++- + 3 files changed, 76 insertions(+), 77 deletions(-) + +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 8e2b839fe89..630d8c7e16a 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + + #define COBJMACROS + +@@ -138,9 +137,9 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ref = 1; + device->priolevel = DSSCL_NORMAL; + device->stopped = 1; +- device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + +- DSOUND_ParseSpeakerConfig(device); ++ device->speaker_config = 0; ++ device->num_speakers = 0; + + /* 3D listener initial parameters */ + device->ds3dl.dwSize = sizeof(DS3DLISTENER); +@@ -1127,75 +1126,3 @@ HRESULT WINAPI DirectSoundCreate8( + + return hr; + } +- +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) +-{ +- switch (DSSPEAKER_CONFIG(device->speaker_config)) { +- case DSSPEAKER_MONO: +- device->speaker_angles[0] = M_PI/180.0f * 0.0f; +- device->speaker_num[0] = 0; +- device->num_speakers = 1; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_STEREO: +- case DSSPEAKER_HEADPHONE: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * 90.0f; +- device->speaker_num[0] = 0; /* Left */ +- device->speaker_num[1] = 1; /* Right */ +- device->num_speakers = 2; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_QUAD: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 45.0f; +- device->speaker_angles[3] = M_PI/180.0f * 135.0f; +- device->speaker_num[0] = 2; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 1; /* Front right */ +- device->speaker_num[3] = 3; /* Rear right */ +- device->num_speakers = 4; +- device->lfe_channel = -1; +- break; +- +- case DSSPEAKER_5POINT1_BACK: +- device->speaker_angles[0] = M_PI/180.0f * -135.0f; +- device->speaker_angles[1] = M_PI/180.0f * -45.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 45.0f; +- device->speaker_angles[4] = M_PI/180.0f * 135.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- case DSSPEAKER_5POINT1_SURROUND: +- device->speaker_angles[0] = M_PI/180.0f * -90.0f; +- device->speaker_angles[1] = M_PI/180.0f * -30.0f; +- device->speaker_angles[2] = M_PI/180.0f * 0.0f; +- device->speaker_angles[3] = M_PI/180.0f * 30.0f; +- device->speaker_angles[4] = M_PI/180.0f * 90.0f; +- device->speaker_angles[5] = 9999.0f; +- device->speaker_num[0] = 4; /* Rear left */ +- device->speaker_num[1] = 0; /* Front left */ +- device->speaker_num[2] = 2; /* Front centre */ +- device->speaker_num[3] = 1; /* Front right */ +- device->speaker_num[4] = 5; /* Rear right */ +- device->speaker_num[5] = 3; /* LFE */ +- device->num_speakers = 6; +- device->lfe_channel = 3; +- break; +- +- default: +- WARN("unknown speaker_config %lu\n", device->speaker_config); +- } +-} +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index 69c4a2f3902..1a3e88a0d29 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); + + /* primary.c */ + +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index 852ec51b7ff..fdbbc00cd33 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -24,6 +24,7 @@ + */ + + #include ++#include + + #define COBJMACROS + #define NONAMELESSUNION +@@ -110,6 +111,78 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) + return def; + } + ++static void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) ++{ ++ switch (DSSPEAKER_CONFIG(device->speaker_config)) { ++ case DSSPEAKER_MONO: ++ device->speaker_angles[0] = M_PI/180.0f * 0.0f; ++ device->speaker_num[0] = 0; ++ device->num_speakers = 1; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_STEREO: ++ case DSSPEAKER_HEADPHONE: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * 90.0f; ++ device->speaker_num[0] = 0; /* Left */ ++ device->speaker_num[1] = 1; /* Right */ ++ device->num_speakers = 2; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_QUAD: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 135.0f; ++ device->speaker_num[0] = 2; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 1; /* Front right */ ++ device->speaker_num[3] = 3; /* Rear right */ ++ device->num_speakers = 4; ++ device->lfe_channel = -1; ++ break; ++ ++ case DSSPEAKER_5POINT1_BACK: ++ device->speaker_angles[0] = M_PI/180.0f * -135.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -45.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 45.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 135.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ case DSSPEAKER_5POINT1_SURROUND: ++ device->speaker_angles[0] = M_PI/180.0f * -90.0f; ++ device->speaker_angles[1] = M_PI/180.0f * -30.0f; ++ device->speaker_angles[2] = M_PI/180.0f * 0.0f; ++ device->speaker_angles[3] = M_PI/180.0f * 30.0f; ++ device->speaker_angles[4] = M_PI/180.0f * 90.0f; ++ device->speaker_angles[5] = 9999.0f; ++ device->speaker_num[0] = 4; /* Rear left */ ++ device->speaker_num[1] = 0; /* Front left */ ++ device->speaker_num[2] = 2; /* Front centre */ ++ device->speaker_num[3] = 1; /* Front right */ ++ device->speaker_num[4] = 5; /* Rear right */ ++ device->speaker_num[5] = 3; /* LFE */ ++ device->num_speakers = 6; ++ device->lfe_channel = 3; ++ break; ++ ++ default: ++ WARN("unknown speaker_config %u\n", device->speaker_config); ++ } ++} ++ + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -124,7 +197,7 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + if (FAILED(hr)) + return hr; + +- if (mixwfe->Format.nChannels < device->num_speakers) { ++ if (device->num_speakers == 0 || mixwfe->Format.nChannels < device->num_speakers) { + device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); + DSOUND_ParseSpeakerConfig(device); + } else if (mixwfe->Format.nChannels > device->num_speakers) { +From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:28 +0200 +Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index 6378a091f0b..e976fda77f2 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, + */ + HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) + { +- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); ++ if (!hwnd) return E_HANDLE; ++ if (!pv_attribute) return E_INVALIDARG; + +- return E_NOTIMPL; ++ switch (attribute) ++ { ++ case DWMWA_NCRENDERING_ENABLED: ++ if (size < sizeof(BOOL)) return E_INVALIDARG; ++ ++ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); ++ *(BOOL*)(pv_attribute) = FALSE; ++ break; ++ ++ case DWMWA_CLOAKED: ++ if (size < sizeof(DWORD)) return E_INVALIDARG; ++ ++ WARN("DWMWA_CLOAKED: always returning 0.\n"); ++ *(DWORD*)(pv_attribute) = 0; ++ break; ++ ++ default: ++ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); ++ return E_INVALIDARG; ++ } ++ ++ return S_OK; + } + + /********************************************************************** +From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Fri, 13 Dec 2019 15:54:30 +0200 +Subject: [PATCH] dwmapi: Add partial implementation of + DWMWA_EXTENDED_FRAME_BOUNDS. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/dwmapi/Makefile.in | 1 + + dlls/dwmapi/dwmapi_main.c | 7 +++++++ + dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in +index 3a3691326f8..d273a22c8f3 100644 +--- a/dlls/dwmapi/Makefile.in ++++ b/dlls/dwmapi/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = dwmapi.dll + IMPORTLIB = dwmapi ++IMPORTS = user32 + + EXTRADLLFLAGS = -Wb,--prefer-native + +diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c +index e976fda77f2..212c88c5a02 100644 +--- a/dlls/dwmapi/dwmapi_main.c ++++ b/dlls/dwmapi/dwmapi_main.c +@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib + *(BOOL*)(pv_attribute) = FALSE; + break; + ++ case DWMWA_EXTENDED_FRAME_BOUNDS: ++ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ++ ++ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); ++ GetWindowRect(hwnd, pv_attribute); ++ break; ++ + case DWMWA_CLOAKED: + if (size < sizeof(DWORD)) return E_INVALIDARG; + +From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 +From: Brendan Shanks +Date: Mon, 13 Apr 2020 16:25:47 -0700 +Subject: [PATCH] HACK: dxgi: Swap around memory sizes for GTA IV + +GTA IV ends up using its "Intel integrated" codepath for determining +VRAM size (since nvapi/atiadlxx fail), but this requires that +DedicatedVideoMemory is a very small dummy value, and SharedSystemMemory +is the actual VRAM size. +Swap the memory values around so this works. +--- + dlls/dxgi/adapter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/dlls/dxgi/adapter.c b/dlls/dxgi/adapter.c +index 5a97590dbce..a5563498cdd 100644 +--- a/dlls/dxgi/adapter.c ++++ b/dlls/dxgi/adapter.c +@@ -200,6 +200,25 @@ static HRESULT dxgi_adapter_get_desc(struct dxgi_adapter *adapter, DXGI_ADAPTER_ + desc->GraphicsPreemptionGranularity = 0; /* FIXME */ + desc->ComputePreemptionGranularity = 0; /* FIXME */ + ++ { ++ /* HACK ++ * ++ * Grand Theft Auto IV first tries to get VRAM size using nvapi/atiadlxx, ++ * after that fails it falls back to the Intel integrated codepath which ++ * uses DXGI. ++ * ++ * DedicatedVideoMemory must be a dummy value less than 200 MB, then ++ * SharedSystemMemory will be used as the VRAM size. ++ * In case of failure, the game will just use 512 MB as VRAM size. ++ */ ++ const char *sgi = getenv("SteamGameId"); ++ if(sgi && !strcmp(sgi, "12210")) ++ { ++ desc->SharedSystemMemory = adapter_id.video_memory; ++ desc->DedicatedVideoMemory = 32 * 1024 * 1024; ++ } ++ } ++ + return hr; + } + +From c619409f235cf660cdd4fd3295d5e04ec628daa1 Mon Sep 17 00:00:00 2001 +From: Alexey Prokhin +Date: Thu, 23 Apr 2020 12:29:55 +0300 +Subject: [PATCH] kernelbase: Set the proper error code in + GetQueuedCompletionStatus{Ex} when the handle is closed. + +Planet Zoo relies on it being ERROR_ABANDONED_WAIT_0. +--- + dlls/kernelbase/sync.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c +index 0ae3aadde92..e95ede8aed5 100644 +--- a/dlls/kernelbase/sync.c ++++ b/dlls/kernelbase/sync.c +@@ -960,6 +960,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD co + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); ++ else if (status == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } +@@ -981,6 +982,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPP + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); ++ else if (ret == ERROR_WAIT_NO_CHILDREN) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; + } + +From 0255dbc3afd3ff673fa701e7802474483252fcb2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 13 Jul 2020 10:21:49 -0500 +Subject: [PATCH] ntdll: Handle NULL object name buffer in + nt_to_unix_file_name_attr(). + +--- + dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++- + dlls/ntdll/unix/file.c | 8 ++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index 20eb6a05922..d99c6b462b2 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -3593,14 +3593,20 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char *unix_name; + int name_len, unix_len; + NTSTATUS status; + ++ if (!attr->ObjectName->Buffer && attr->ObjectName->Length) ++ return STATUS_ACCESS_VIOLATION; ++ + fileobj.FileName = *attr->ObjectName; + reparse: + if (reparse_count++ == 31) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + if (!rootdir) /* without root dir fall back to normal lookup */ + { ++ if (!attr->ObjectName->Buffer) ++ return STATUS_OBJECT_PATH_SYNTAX_BAD; ++ + status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition ); + if (status == STATUS_REPARSE) goto reparse; + if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer); + return status; +From 514d3e11c999b11a95ab35df5b4ab454d34fe791 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 13 May 2020 13:55:55 +0300 +Subject: [PATCH] ntdll: Add WINE_DISABLE_WRITE_WATCH env var to disable write + watch support. + +Massively improves performance for corert games (Streets of Rage 4). +Could be fixed properly with Linux kernel changes. +--- + dlls/ntdll/unix/virtual.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7ea80852090..272e1c5b176 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3425,6 +3425,22 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (!is_win64 && !is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + ++ if (type & MEM_WRITE_WATCH) ++ { ++ static int disable = -1; ++ ++ if (disable == -1) ++ { ++ const char *env_var; ++ ++ if ((disable = (env_var = getenv("WINE_DISABLE_WRITE_WATCH")) && atoi(env_var))) ++ FIXME("Disabling write watch support.\n"); ++ } ++ ++ if (disable) ++ return STATUS_NOT_SUPPORTED; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +From 912701cb515acdc8e0ff263552f656cd49f60714 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 13:01:26 +0200 +Subject: [PATCH] d3d10core: Implement D3D10CoreRegisterLayers. + +--- + dlls/d3d10core/d3d10core_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/dlls/d3d10core/d3d10core_main.c b/dlls/d3d10core/d3d10core_main.c +index d364be90d6d..5a9a4a211c1 100644 +--- a/dlls/d3d10core/d3d10core_main.c ++++ b/dlls/d3d10core/d3d10core_main.c +@@ -29,11 +29,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d10core); + HRESULT WINAPI D3D11CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, unsigned int flags, + const D3D_FEATURE_LEVEL *feature_levels, unsigned int level_count, ID3D11Device **device); + ++HRESULT WINAPI D3D11CoreRegisterLayers(void); ++ + HRESULT WINAPI D3D10CoreRegisterLayers(void) + { + TRACE("\n"); + +- return E_NOTIMPL; ++ return D3D11CoreRegisterLayers(); + } + + HRESULT WINAPI D3D10CoreCreateDevice(IDXGIFactory *factory, IDXGIAdapter *adapter, + +From 376a037ce1391e071835a0bf30ebf1fcec43c367 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 15 May 2020 12:59:44 +0200 +Subject: [PATCH] dxgi: Add fallback to D3D10CoreRegisterLayers. + +If D3D11CoreRegisterLayers is not found in module. +--- + dlls/dxgi/dxgi_main.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..006ab370844 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -106,8 +106,8 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + + if (!dxgi_main.d3d10core) + { +- HRESULT hr; +- HRESULT (WINAPI *d3d11core_register_layers)(void); ++ HRESULT hr = E_FAIL; ++ HRESULT (WINAPI *register_layers)(void); + HMODULE mod; + BOOL ret; + +@@ -117,8 +117,10 @@ static HRESULT register_d3d10core_layers(HMODULE d3d10core) + return E_FAIL; + } + +- d3d11core_register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers"); +- hr = d3d11core_register_layers(); ++ if ((register_layers = (void *)GetProcAddress(mod, "D3D11CoreRegisterLayers")) || ++ (register_layers = (void *)GetProcAddress(mod, "D3D10CoreRegisterLayers"))) ++ hr = register_layers(); ++ + if (FAILED(hr)) + { + ERR("Failed to register d3d11 layers, returning %#x.\n", hr); + +From 5b00ca69a1927e1d0df4cf26160228e460568c51 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 9 Jun 2020 14:16:22 +0300 +Subject: [PATCH] kernelbase: HACK Add an option to blacklist files. + +--- + dlls/kernelbase/file.c | 76 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 75 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index eb2ef57c7d6..c7881fe4516 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -41,6 +41,8 @@ + #include "wine/exception.h" + #include "wine/debug.h" + ++#include "wine/heap.h" ++ + WINE_DEFAULT_DEBUG_CHANNEL(file); + + /* info structure for FindFirstFile handle */ +@@ -432,6 +434,72 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sh + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); + } + ++#define MAX_BLACKLISTED_FILENAMES 32 ++ ++static struct ++{ ++ const WCHAR *name; ++ size_t name_len; ++} ++blacklist_filenames[MAX_BLACKLISTED_FILENAMES]; ++ ++static unsigned int blacklist_filename_count; ++ ++static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) ++{ ++ const WCHAR separators[] = L",; "; ++ WCHAR *buffer, *token; ++ DWORD size; ++ ++ if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) ++ return TRUE; ++ ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } ++ ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ ++ blacklist_filename_count = 0; ++ token = wcstok(buffer, separators); ++ while (token && blacklist_filename_count < MAX_BLACKLISTED_FILENAMES) ++ { ++ FIXME("Blacklisting %s file.\n", debugstr_w(token)); ++ blacklist_filenames[blacklist_filename_count].name = token; ++ blacklist_filenames[blacklist_filename_count++].name_len = wcslen(token); ++ token = wcstok(NULL, separators); ++ } ++ ++ if (token && blacklist_filename_count == MAX_BLACKLISTED_FILENAMES) ++ ERR("File black list is too long.\n"); ++ ++ return TRUE; ++} ++ ++static BOOL is_file_blacklisted(LPCWSTR filename) ++{ ++ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; ++ unsigned int i; ++ size_t len; ++ ++ if (!InitOnceExecuteOnce(&init_once, init_file_blacklist, NULL, NULL)) ++ return FALSE; ++ ++ len = wcslen(filename); ++ ++ for (i = 0; i < blacklist_filename_count; ++i) ++ if (blacklist_filenames[i].name_len <= len ++ && !wcsicmp(blacklist_filenames[i].name, filename + len - blacklist_filenames[i].name_len)) ++ return TRUE; ++ ++ return FALSE; ++} + + /************************************************************************* + * CreateFileW (kernelbase.@) +@@ -460,7 +528,6 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + +- + /* sanity checks */ + + if (!filename || !filename[0]) +@@ -479,6 +546,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + ++ if (is_file_blacklisted(filename)) ++ { ++ FIXME("\"%s\" is blacklisted.\n", debugstr_w(filename)); ++ SetLastError( ERROR_FILE_NOT_FOUND ); ++ return INVALID_HANDLE_VALUE; ++ } ++ + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + +From 2409bd1f74be116172688a25df725290637c255a Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 10 Jun 2020 16:18:29 -0500 +Subject: [PATCH] kernelbase: Apply blacklist automatically to Origin + executables + +--- + dlls/kernelbase/file.c | 54 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index c7881fe4516..bb4be45da5d 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -447,23 +447,57 @@ static unsigned int blacklist_filename_count; + + static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, PVOID *context) + { ++ static WCHAR origin_blacklist[] = L"kernel32.dll;user32.dll"; ++ + const WCHAR separators[] = L",; "; + WCHAR *buffer, *token; + DWORD size; + +- if (!(size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) +- return TRUE; +- +- if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ if ((size = GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", NULL, 0))) + { +- ERR("No memory.\n"); +- return FALSE; +- } ++ if (!(buffer = heap_alloc(sizeof(*buffer) * size))) ++ { ++ ERR("No memory.\n"); ++ return FALSE; ++ } + +- if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ if (GetEnvironmentVariableW(L"WINE_BLACKLIST_FILES", buffer, size) != size - 1) ++ { ++ ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); ++ return FALSE; ++ } ++ } ++ else + { +- ERR("Error getting WINE_BLACKLIST_FILES env variable.\n"); +- return FALSE; ++ static const WCHAR *origin_names[] = { ++ L"igoproxy64.exe", ++ L"igoproxy.exe", ++ L"origin.exe", ++ L"easteamproxy.exe" ++ }; ++ ++ WCHAR cur_exe[MAX_PATH]; ++ DWORD cur_exe_len, i; ++ ++ if (!(cur_exe_len = GetModuleFileNameW(NULL, cur_exe, ARRAY_SIZE(cur_exe)))) ++ return TRUE; ++ ++ buffer = NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(origin_names); ++i) ++ { ++ DWORD origin_name_len = wcslen(origin_names[i]); ++ if (cur_exe_len >= origin_name_len && ++ wcsicmp(cur_exe + cur_exe_len - origin_name_len, origin_names[i]) == 0) ++ { ++ FIXME("using origin file blacklist for %s\n", debugstr_w(cur_exe)); ++ buffer = origin_blacklist; ++ break; ++ } ++ } ++ ++ if (!buffer) ++ return TRUE; + } + + blacklist_filename_count = 0; +From ff790e8d99f2026af9b8569355fc1df5e6f0c639 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Sat, 5 Sep 2020 01:09:37 +0300 +Subject: [PATCH] wbemprox: HACK: Make Bloons TD6 happy so it does not exit + after getting string Wine from bios info. + +--- + dlls/wbemprox/builtin.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c +index 43268221936..f39d6f0cd7b 100644 +--- a/dlls/wbemprox/builtin.c ++++ b/dlls/wbemprox/builtin.c +@@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) + static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -1307,7 +1307,7 @@ static enum fill_status fill_bios( struct table *table, const struct expr *cond + rec->smbiosminorversion = get_bios_smbiosminorversion( buf, len ); + rec->systembiosmajorversion = get_bios_system_bios_major_release( buf, len ); + rec->systembiosminorversion = get_bios_system_bios_minor_release( buf, len ); +- rec->version = L"WINE - 1"; ++ rec->version = L"PROTON - 1"; + if (!match_row( table, row, cond, &status )) free_row_values( table, row ); + else row++; + +@@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + + static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); +- if (!ret) return wcsdup( L"The Wine Project" ); ++ if (!ret) return wcsdup( L"The Proton Project" ); + return ret; + } + +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e + rec->index = aa->u.s.IfIndex; + rec->interface_index = aa->u.s.IfIndex; + rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ + rec->name = wcsdup( aa->FriendlyName ); + rec->netenabled = connection_status ? -1 : 0; +@@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct + rec->lastbootuptime = get_lastbootuptime(); + rec->localdatetime = get_localdatetime(); + rec->locale = get_locale(); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = get_osname( rec->caption ); + rec->operatingsystemsku = get_operatingsystemsku(); + rec->osarchitecture = get_osarchitecture(); +@@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) + static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) + { + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); +- if (!ret) return wcsdup( L"Wine" ); ++ if (!ret) return wcsdup( L"Proton" ); + return ret; + } + +@@ -3955,7 +3955,7 @@ static enum fill_status fill_sounddevice( struct table *table, const struct expr + + rec = (struct record_sounddevice *)table->data; + rec->deviceid = get_sounddevice_pnpdeviceid( &desc ); +- rec->manufacturer = L"The Wine Project"; ++ rec->manufacturer = L"The Proton Project"; + rec->name = L"Wine Audio Device"; + rec->pnpdeviceid = get_sounddevice_pnpdeviceid( &desc ); + rec->productname = L"Wine Audio Device"; +-- +2.26.2 + +From 87326687df23529255c2493d178dac1310919980 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 4 Nov 2020 18:08:21 +0300 +Subject: [PATCH] ws2_32: HACK Fail 'download-alt.easyanticheat.net' DNS name + resolution. + +This has the same effect as the following reverted ECDHE-ECDSA patch, +but allows those ciphers to be used for other programs. +--- + dlls/ws2_32/protocol.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/protocol.c ++++ b/dlls/ws2_32/protocol.c +@@ -232,6 +232,13 @@ int WINAPI WS_getaddrinfo(LPCSTR nodename, LPCSTR servname, const struct WS_addr + return WSAHOST_NOT_FOUND; + } + ++ if (node && !strcmp(node, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ SetLastError(WSAHOST_NOT_FOUND); ++ return WSAHOST_NOT_FOUND; ++ } ++ + if (node) + { + if (!node[0]) +diff --git a/dlls/ws2_32/unixlib.c b/dlls/ws2_32/unixlib.c +index 4010a727a08..ef9a640a771 100644 +--- a/dlls/ws2_32/unixlib.c ++++ b/dlls/ws2_32/unixlib.c +@@ -869,6 +869,12 @@ static int CDECL unix_gethostbyname( const char *name, struct WS_hostent *const + int locerr; + int ret; + ++ if (!strcmp(params->name, "download-alt.easyanticheat.net")) ++ { ++ ERR("HACK: failing download-alt.easyanticheat.net resolution.\n"); ++ return HOST_NOT_FOUND; ++ } ++ + if (!(unix_buffer = malloc( unix_size ))) + return WSAENOBUFS; + +From b48ce9a2778a4ff7ee14067f0ea73693ed70c52d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 20 Jun 2018 15:10:08 -0500 +Subject: [PATCH] server: Don't check for a hung queue when sending low-level + hooks. + +Since user32 does this. + +This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm. +--- + server/queue.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/server/queue.c b/server/queue.c +index 968f2ffe701..aa8e627211a 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -1641,7 +1641,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa + + if (!(hook_thread = get_first_global_hook( id ))) return 0; + if (!(queue = hook_thread->queue)) return 0; +- if (is_queue_hung( queue )) return 0; + + if (!(msg = mem_alloc( sizeof(*msg) ))) return 0; + +From c9a5fcb0966ab0ca0722c8c07476c131844f98e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 16 Oct 2020 23:37:09 +0200 +Subject: [PATCH] dotnetfx35.exe: Add stub program. + +This makes it possible to override native dotnetfx35 installer, which +is broken in an unfixable way. Recent Windows versions also bypass its +execution somehow. +--- + configure | 2 ++ + configure.ac | 1 + + programs/dotnetfx35/Makefile.in | 7 +++++++ + programs/dotnetfx35/main.c | 32 ++++++++++++++++++++++++++++++++ + 4 files changed, 42 insertions(+) + create mode 100644 programs/dotnetfx35/Makefile.in + create mode 100644 programs/dotnetfx35/main.c + +diff --git a/configure b/configure +index 848323bb057..d676b1c82ee 100755 +--- a/configure ++++ b/configure +@@ -1766,6 +1766,7 @@ enable_conhost + enable_cscript + enable_dism + enable_dllhost ++enable_dotnetfx35 + enable_dplaysvr + enable_dpnsvr + enable_dpvsetup +@@ -21579,6 +21580,7 @@ wine_fn_config_makefile programs/conhost enable_conhost + wine_fn_config_makefile programs/cscript enable_cscript + wine_fn_config_makefile programs/dism enable_dism + wine_fn_config_makefile programs/dllhost enable_dllhost ++wine_fn_config_makefile programs/dotnetfx35 enable_dotnetfx35 + wine_fn_config_makefile programs/dplaysvr enable_dplaysvr + wine_fn_config_makefile programs/dpnsvr enable_dpnsvr + wine_fn_config_makefile programs/dpvsetup enable_dpvsetup +diff --git a/configure.ac b/configure.ac +index 130dbeb8530..63d26ff123c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3997,6 +3997,7 @@ WINE_CONFIG_MAKEFILE(programs/conhost) + WINE_CONFIG_MAKEFILE(programs/cscript) + WINE_CONFIG_MAKEFILE(programs/dism) + WINE_CONFIG_MAKEFILE(programs/dllhost) ++WINE_CONFIG_MAKEFILE(programs/dotnetfx35) + WINE_CONFIG_MAKEFILE(programs/dplaysvr) + WINE_CONFIG_MAKEFILE(programs/dpnsvr) + WINE_CONFIG_MAKEFILE(programs/dpvsetup) +diff --git a/programs/dotnetfx35/Makefile.in b/programs/dotnetfx35/Makefile.in +new file mode 100644 +index 00000000000..e50ed37f700 +--- /dev/null ++++ b/programs/dotnetfx35/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = dotnetfx35.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -mno-cygwin ++ ++SOURCES = \ ++ main.c +diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c +new file mode 100644 +index 00000000000..cd6df5bcf41 +--- /dev/null ++++ b/programs/dotnetfx35/main.c +@@ -0,0 +1,32 @@ ++/* ++ * Fake dotnetfx35.exe installer ++ * ++ * Copyright 2020 RĂ©mi Bernon ++ * ++ * 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 ++ */ ++ ++#include ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dotnetfx); ++ ++int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ++{ ++ FIXME("stub!"); ++ return 0; ++} +From 8a0f4add2096d213999d9be7a04229d288f1af75 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 24 Aug 2018 14:41:44 -0500 +Subject: [PATCH] HACK: winex11: Grab mouse in fullscreen windows by default + +--- + dlls/winex11.drv/x11drv_main.c | 2 +- + programs/winecfg/x11drvdlg.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c +index af2094a96f0..9c98209d43d 100644 +--- a/dlls/winex11.drv/x11drv_main.c ++++ b/dlls/winex11.drv/x11drv_main.c +@@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_primary_selection = FALSE; + BOOL use_system_cursors = TRUE; + BOOL show_systray = TRUE; +-BOOL grab_fullscreen = FALSE; ++BOOL grab_fullscreen = TRUE; + int keyboard_layout = -1; + BOOL keyboard_scancode_detect = FALSE; + BOOL managed_mode = TRUE; +diff --git a/programs/winecfg/input.c b/programs/winecfg/input.c +index fbc6716e94e..1ca8c3ed0fd 100644 +--- a/programs/winecfg/input.c ++++ b/programs/winecfg/input.c +@@ -90,7 +90,7 @@ static void init_dialog(HWND dialog) + + updating_ui = TRUE; + +- buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"N" ); ++ buffer = get_reg_key( config_key, keypath( L"X11 Driver" ), L"GrabFullscreen", L"Y" ); + if (IS_OPTION_TRUE( *buffer )) CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_CHECKED ); + else CheckDlgButton( dialog, IDC_FULLSCREEN_GRAB, BST_UNCHECKED ); + free( buffer ); +From 2452b57e7fed9ac5036df9bcace5c28366a8eb41 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 8 Jan 2021 13:47:39 -0600 +Subject: [PATCH] HACK: kernelbase: Add NFS EXE to Origin file blacklist hack + +--- + dlls/kernelbase/file.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c +index a33440107b6..397aa64dce2 100644 +--- a/dlls/kernelbase/file.c ++++ b/dlls/kernelbase/file.c +@@ -741,7 +741,8 @@ static BOOL CALLBACK init_file_blacklist(PINIT_ONCE init_once, PVOID parameter, + L"igoproxy64.exe", + L"igoproxy.exe", + L"origin.exe", +- L"easteamproxy.exe" ++ L"easteamproxy.exe", ++ L"NFS11Remastered.exe" + }; + + WCHAR cur_exe[MAX_PATH]; +From e6c7aa8bc42fea042c3d504009ad8a0fb70a6c0c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 21 Jan 2021 11:54:10 -0600 +Subject: [PATCH] vulkan-1: Prefer builtin + +Games that ship their own vulkan-1 will be broken with your VR wrappers. +--- + dlls/vulkan-1/Makefile.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/dlls/vulkan-1/Makefile.in b/dlls/vulkan-1/Makefile.in +index a4a10bc8e93..551ef146cd7 100644 +--- a/dlls/vulkan-1/Makefile.in ++++ b/dlls/vulkan-1/Makefile.in +@@ -2,6 +2,4 @@ MODULE = vulkan-1.dll + IMPORTS = winevulkan + IMPORTLIB = vulkan-1 + +-EXTRADLLFLAGS = -Wb,--prefer-native +- + SOURCES = \ + version.rc \ + vulkan.c +From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 25 Mar 2021 17:53:37 +0300 +Subject: [PATCH 07/16] wine.inf: Create package repository for VCLibs.140. + +--- + loader/wine.inf.in | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4df88d1f386..1506a73573b 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -91,10 +91,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo,\ + LicenseInformation +@@ -117,10 +118,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntamd64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -142,10 +144,11 @@ AddReg=\ + MCI,\ + Misc,\ + OLE,\ ++ Packages.ntarm64,\ + Printing,\ + Services, \ + SessionMgr,\ + Tapi,\ + ThemeManager,\ + VersionInfo.ntamd64,\ + LicenseInformation +@@ -163,6 +166,7 @@ AddReg=\ + MCI,\ + Misc,\ ++ Packages.wow64,\ + Tapi,\ + VersionInfo,\ + LicenseInformation + +@@ -247,6 +251,7 @@ CurrentVersion="Software\Microsoft\Windows\CurrentVersion" + CurrentVersionNT="Software\Microsoft\Windows NT\CurrentVersion" + FontSubStr="Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" + Control="System\CurrentControlSet\Control" ++Packages="Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages" + + [Classes] + HKCR,.chm,,2,"chm.file" +@@ -597,6 +602,18 @@ [OLE] + HKLM,"Software\Microsoft\OLE","EnableDCOM",,"Y" + HKLM,"Software\Microsoft\OLE","EnableRemoteConnect",,"N" + ++[Packages] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.ntamd64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ ++[Packages.wow64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_x86__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\syswow64" ++ ++[Packages.arm64] ++HKLM,"%Packages%\Microsoft.VCLibs.140.00_14.0.29231.0_arm64__8wekyb3d8bbwe","Path",0x00020002,"%SystemRoot%\system32" ++ + [Printing] + HKLM,%Control%\Print\Monitors\Local Port,"Driver",2,"localspl.dll" + HKLM,%Control%\Print\Printers,"DefaultSpoolDirectory",2,"%11%\spool\printers" +-- +2.30.2 + +From 4a2ca2d4e87c84a57dde064296822860701fc877 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 21:59:00 +0300 +Subject: [PATCH 08/16] kernel32: Implement GetPackagesByPackageFamily(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 115 +++++++++++++++ + include/appmodel.h | 2 + + 5 files changed, 250 insertions(+), 1 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index bcaf2512990..59785a6de24 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -765,6 +765,7 @@ + @ stdcall -import GetOverlappedResultEx(long ptr ptr long long) + @ stdcall -import GetUserDefaultGeoName(ptr long) + @ stdcall -import GetUserPreferredUILanguages(long ptr ptr ptr) ++@ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index b42a9af8b8e..5d21680f512 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -614,7 +614,7 @@ + # @ stub GetPackageStatusForUser + # @ stub GetPackageTargetPlatformProperty + # @ stub GetPackageVolumeSisPath +-# @ stub GetPackagesByPackageFamily ++@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPerformanceInfo(ptr long) + @ stdcall GetPhysicallyInstalledSystemMemory(ptr) + # @ stub GetPreviousFgPolicyRefreshInfoInternal +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 66bd619e394..dca4ffb8647 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -39,10 +39,12 @@ + #include "winnls.h" + #include "winternl.h" + #include "winerror.h" ++#include "winreg.h" + #include "appmodel.h" + + #include "kernelbase.h" + #include "wine/debug.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(ver); + +@@ -154,6 +156,8 @@ static const struct + } + }; + ++static const WCHAR packages_key_name[] = L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows" ++ L"\\CurrentVersion\\AppModel\\PackageRepository\\Packages"; + + /****************************************************************************** + * init_current_version +@@ -1681,3 +1685,114 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + + return ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagesByPackageFamily (kernelbase.@) ++ */ ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer) ++{ ++ UINT32 curr_count, curr_length, package_id_buf_size, size; ++ unsigned int i, name_len, publisher_id_len; ++ DWORD subkey_count, max_key_len, length; ++ const WCHAR *publisher_id; ++ WCHAR *package_name; ++ BOOL short_buffer; ++ PACKAGE_ID *id; ++ HKEY key; ++ ++ TRACE("family_name %s, count %p, full_names %p, buffer_length %p, buffer %p.\n", ++ debugstr_w(family_name), count, full_names, buffer_length, buffer); ++ ++ if (!buffer_length || !count || !family_name) ++ return ERROR_INVALID_PARAMETER; ++ ++ if ((*buffer_length || *count) && (!full_names || !buffer)) ++ return ERROR_INVALID_PARAMETER; ++ ++ if (!(publisher_id = wcschr(family_name, L'_'))) ++ return ERROR_INVALID_PARAMETER; ++ ++ name_len = publisher_id - family_name; ++ ++publisher_id; ++ publisher_id_len = lstrlenW(publisher_id); ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, packages_key_name, 0, KEY_READ, &key)) ++ { ++ ERR("Key open failed.\n"); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, &subkey_count, &max_key_len, NULL, NULL, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Query key info failed.\n"); ++ RegCloseKey(key); ++ *count = 0; ++ *buffer_length = 0; ++ return ERROR_SUCCESS; ++ } ++ ++ if (!(package_name = heap_alloc((max_key_len + 1) * sizeof(*package_name)))) ++ { ++ ERR("No memory.\n"); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ package_id_buf_size = sizeof(*id) + (max_key_len + 1) * sizeof(WCHAR); ++ if (!(id = heap_alloc(package_id_buf_size))) ++ { ++ ERR("No memory.\n"); ++ heap_free(package_name); ++ RegCloseKey(key); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ curr_count = curr_length = 0; ++ for (i = 0; i < subkey_count; ++i) ++ { ++ length = max_key_len + 1; ++ if (RegEnumKeyExW(key, i, package_name, &length, NULL, NULL, NULL, NULL)) ++ { ++ ERR("Error enumerating key %u.\n", i); ++ continue; ++ } ++ ++ size = package_id_buf_size; ++ if (PackageIdFromFullName(package_name, 0, &size, (BYTE *)id)) ++ { ++ ERR("Error getting package id from full name.\n"); ++ continue; ++ } ++ ++ if (lstrlenW(id->name) != name_len) ++ continue; ++ if (wcsnicmp(family_name, id->name, name_len)) ++ continue; ++ ++ if (lstrlenW(id->publisherId) != publisher_id_len) ++ continue; ++ if (wcsnicmp(publisher_id, id->publisherId, publisher_id_len)) ++ continue; ++ if (curr_length + length < *buffer_length) ++ { ++ memcpy(buffer + curr_length, package_name, (length + 1) * sizeof(*package_name)); ++ if (curr_count < *count) ++ full_names[curr_count] = buffer + curr_length; ++ } ++ curr_length += length + 1; ++ ++curr_count; ++ } ++ ++ heap_free(id); ++ heap_free(package_name); ++ RegCloseKey(key); ++ ++ short_buffer = curr_length > *buffer_length || curr_count > *count; ++ *count = curr_count; ++ *buffer_length = curr_length; ++ ++ return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index e4288bbfbb0..27a0d0a8646 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,8 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, ++ UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From 1bf233880b472769e5b560a3d50adbbecfeb5736 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 26 Mar 2021 01:10:08 +0300 +Subject: [PATCH 09/16] kernel32: Implement PackageFullNameFromId(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 43 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 67 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 59785a6de24..491f5ca0402 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -1152,6 +1152,7 @@ + @ stdcall -import PeekConsoleInputW(ptr ptr long ptr) + @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr) + @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr) ++@ stdcall -import PackageFullNameFromId(ptr ptr ptr) + @ stdcall -import PackageIdFromFullName(wstr long ptr ptr) + @ stdcall PowerClearRequest(long long) + @ stdcall PowerCreateRequest(ptr) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d21680f512..81d88b1a33a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -1012,7 +1012,7 @@ + # @ stub PackageFamilyNameFromFullName + # @ stub PackageFamilyNameFromId + # @ stub PackageFamilyNameFromProductId +-# @ stub PackageFullNameFromId ++@ stdcall PackageFullNameFromId(ptr ptr ptr) + # @ stub PackageFullNameFromProductId + @ stdcall PackageIdFromFullName(wstr long ptr ptr) + # @ stub PackageIdFromProductId +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index dca4ffb8647..560e45e965a 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1597,6 +1597,16 @@ static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) + return ~0u; + } + ++const WCHAR *string_from_processor_arch(UINT32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(arch_names); ++i) ++ if (code == arch_names[i].code) ++ return arch_names[i].name; ++ return NULL; ++} ++ + /*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +@@ -1687,6 +1697,39 @@ LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 * + } + + ++/*********************************************************************** ++ * PackageFullNameFromId (kernelbase.@) ++ */ ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name) ++{ ++ WCHAR ver_str[5 * 4 + 3 + 1]; ++ const WCHAR *arch_str; ++ UINT32 have_length; ++ ++ TRACE("package_id %p, length %p, full_name %p.\n", package_id, length, full_name); ++ ++ if (!package_id || !length) ++ return ERROR_INVALID_PARAMETER; ++ if (!full_name && *length) ++ return ERROR_INVALID_PARAMETER; ++ if (!package_id->name || !package_id->resourceId || !package_id->publisherId ++ || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) ++ return ERROR_INVALID_PARAMETER; ++ ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); ++ have_length = *length; ++ *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 ++ + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; ++ ++ if (have_length < *length) ++ return ERROR_INSUFFICIENT_BUFFER; ++ ++ swprintf(full_name, *length, L"%s_%s_%s_%s_%s", package_id->name, ver_str, arch_str, package_id->resourceId, package_id->publisherId); ++ return ERROR_SUCCESS; ++} ++ ++ + /*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +diff --git a/include/appmodel.h b/include/appmodel.h +index 27a0d0a8646..be59bc70f5f 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -84,6 +84,7 @@ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadIn + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); ++LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); + LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer); + + #if defined(__cplusplus) +-- +2.30.2 + +From fb64ea93325eb2d4aa9be77c3e2b07ca20052c3d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 15 Mar 2021 22:05:19 +0300 +Subject: [PATCH 10/16] kernel32: Implement GetPackagePath(). + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 82 +++++++++++++++++++ + include/appmodel.h | 1 + + 5 files changed, 144 insertions(+), 6 deletions(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index 491f5ca0402..6b1030b00df 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -768,6 +768,7 @@ + @ stdcall -import GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName ++@ stdcall -import GetPackagePath(ptr long ptr ptr) + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 81d88b1a33a..0b374a53b60 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -601,7 +601,7 @@ + # @ stub GetPackageInfo + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested +-# @ stub GetPackagePath ++@ stdcall GetPackagePath(ptr long ptr ptr) + # @ stub GetPackagePathByFullName + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index 560e45e965a..1f16194a565 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1839,3 +1839,85 @@ LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + + return short_buffer ? ERROR_INSUFFICIENT_BUFFER : ERROR_SUCCESS; + } ++ ++ ++/*********************************************************************** ++ * GetPackagePath (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path) ++{ ++ WCHAR *key_name = NULL, *expanded_path = NULL; ++ UINT32 required_length, have_length; ++ unsigned int offset; ++ HKEY key = NULL; ++ DWORD size; ++ LONG ret; ++ ++ TRACE("package_id %p, reserved %u, length %p, path %p.\n", package_id, reserved, length, path); ++ ++ if (!length) ++ return ERROR_INVALID_PARAMETER; ++ if (!path && *length) ++ return ERROR_INVALID_PARAMETER; ++ ++ required_length = 0; ++ if ((ret = PackageFullNameFromId(package_id, &required_length, NULL)) != ERROR_INSUFFICIENT_BUFFER) ++ return ret; ++ ++ offset = lstrlenW(packages_key_name) + 1; ++ if (!(key_name = heap_alloc((offset + required_length) * sizeof(WCHAR)))) ++ { ++ ERR("No memory."); ++ return ERROR_OUTOFMEMORY; ++ } ++ ++ if ((ret = PackageFullNameFromId(package_id, &required_length, key_name + offset))) ++ goto done; ++ ++ memcpy(key_name, packages_key_name, (offset - 1) * sizeof(WCHAR)); ++ key_name[offset - 1] = L'\\'; ++ ++ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key)) ++ { ++ WARN("Key %s not found.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, NULL, &size)) ++ { ++ WARN("Path value not found in %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ if (!(expanded_path = heap_alloc(size))) ++ { ++ ERR("No memory."); ++ ret = ERROR_OUTOFMEMORY; ++ goto done; ++ } ++ if (RegGetValueW(key, NULL, L"Path", RRF_RT_REG_SZ, NULL, expanded_path, &size)) ++ { ++ WARN("Could not get Path value from %s.\n", debugstr_w(key_name)); ++ ret = ERROR_NOT_FOUND; ++ goto done; ++ } ++ ++ have_length = *length; ++ *length = lstrlenW(expanded_path) + 1; ++ if (have_length >= *length) ++ { ++ memcpy(path, expanded_path, *length * sizeof(*path)); ++ ret = ERROR_SUCCESS; ++ } ++ else ++ { ++ ret = ERROR_INSUFFICIENT_BUFFER; ++ } ++ ++done: ++ if (key) ++ RegCloseKey(key); ++ heap_free(expanded_path); ++ heap_free(key_name); ++ return ret; ++} +diff --git a/include/appmodel.h b/include/appmodel.h +index be59bc70f5f..c73cb8d26ef 100644 +--- a/include/appmodel.h ++++ b/include/appmodel.h +@@ -82,6 +82,7 @@ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessT + LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy); + LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy); + LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy); ++LONG WINAPI GetPackagePath(const PACKAGE_ID *package_id, const UINT32 reserved, UINT32 *length, WCHAR *path); + LONG WINAPI GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, WCHAR **full_names, + UINT32 *buffer_length, WCHAR *buffer); + LONG WINAPI PackageFullNameFromId(const PACKAGE_ID *package_id, UINT32 *length, WCHAR *full_name); +-- +2.30.2 + +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 47be204baf902d876fc5e5b0240b6167500ec0dd Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 29 Mar 2021 21:54:30 +0300 +Subject: [PATCH] dxgi: Use proxy IDXGISwapChain interface when importing that + from d3d implementation. + +For Origin overlay. + +Required for Origin overlay to work (which expects IDXGISwapChain +implementation methods to reside in dxgi.dll). +--- + dlls/dxgi/factory.c | 511 +++++++++++++++++++++++++++++++++++++++++- + dlls/dxgi/swapchain.c | 6 +- + 2 files changed, 509 insertions(+), 8 deletions(-) + +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index af18bdd2c32..020faa26a11 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -29,7 +29,7 @@ static inline struct dxgi_factory *impl_from_IWineDXGIFactory(IWineDXGIFactory * + return CONTAINING_RECORD(iface, struct dxgi_factory, IWineDXGIFactory_iface); + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_QueryInterface(IWineDXGIFactory *iface, REFIID iid, void **out) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + +@@ -58,7 +58,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IWineDXGIFactory *i + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_AddRef(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedIncrement(&factory->refcount); +@@ -68,7 +68,7 @@ static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IWineDXGIFactory *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IWineDXGIFactory *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_Release(IWineDXGIFactory *iface) + { + struct dxgi_factory *factory = impl_from_IWineDXGIFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); +@@ -267,7 +267,494 @@ static BOOL STDMETHODCALLTYPE dxgi_factory_IsWindowedStereoEnabled(IWineDXGIFact + return FALSE; + } + +-static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, ++struct proxy_swapchain ++{ ++ IDXGISwapChain4 IDXGISwapChain4_iface; ++ IDXGISwapChain4 *swapchain; ++}; ++ ++static inline struct proxy_swapchain *proxy_swapchain_from_IDXGISwapChain4(IDXGISwapChain4 *iface) ++{ ++ return CONTAINING_RECORD(iface, struct proxy_swapchain, IDXGISwapChain4_iface); ++} ++ ++/* IUnknown methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_QueryInterface(IDXGISwapChain4 *iface, REFIID riid, void **object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ TRACE("iface %p, riid %s, object %p\n", iface, debugstr_guid(riid), object); ++ ++ if (IsEqualGUID(riid, &IID_IUnknown) ++ || IsEqualGUID(riid, &IID_IDXGIObject) ++ || IsEqualGUID(riid, &IID_IDXGIDeviceSubObject) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain1) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain2) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain3) ++ || IsEqualGUID(riid, &IID_IDXGISwapChain4)) ++ { ++ IDXGISwapChain4_AddRef(swapchain->swapchain); ++ *object = iface; ++ return S_OK; ++ } ++ ++ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid)); ++ ++ *object = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_AddRef(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("swapchain %p.\n", swapchain); ++ ++ return IDXGISwapChain4_AddRef(swapchain->swapchain); ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Release(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ULONG refcount = IDXGISwapChain4_Release(swapchain->swapchain); ++ ++ TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); ++ ++ if (!refcount) ++ free(swapchain); ++ ++ return refcount; ++} ++ ++/* IDXGIObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT data_size, const void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_SetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetPrivateDataInterface(IDXGISwapChain4 *iface, ++ REFGUID guid, const IUnknown *object) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, object %p.\n", iface, debugstr_guid(guid), object); ++ ++ return IDXGISwapChain4_SetPrivateDataInterface(swapchain->swapchain, guid, object); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetPrivateData(IDXGISwapChain4 *iface, ++ REFGUID guid, UINT *data_size, void *data) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); ++ ++ return IDXGISwapChain4_GetPrivateData(swapchain->swapchain, guid, data_size, data); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetParent(IDXGISwapChain4 *iface, REFIID riid, void **parent) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, parent %p.\n", iface, debugstr_guid(riid), parent); ++ ++ return IDXGISwapChain4_GetParent(swapchain->swapchain, riid, parent); ++} ++ ++/* IDXGIDeviceSubObject methods */ ++ ++static HRESULT STDMETHODCALLTYPE proxy_swapchain_GetDevice(IDXGISwapChain4 *iface, REFIID riid, void **device) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, riid %s, device %p.\n", iface, debugstr_guid(riid), device); ++ ++ return IDXGISwapChain4_GetDevice(swapchain->swapchain, riid, device); ++} ++ ++/* IDXGISwapChain methods */ ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags); ++ ++ return IDXGISwapChain4_Present(swapchain->swapchain, sync_interval, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBuffer(IDXGISwapChain4 *iface, ++ UINT buffer_idx, REFIID riid, void **surface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBuffer(swapchain->swapchain, buffer_idx, riid, surface); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL fullscreen, IDXGIOutput *target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenState(IDXGISwapChain4 *iface, ++ BOOL *fullscreen, IDXGIOutput **target) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenState(swapchain->swapchain, fullscreen, target); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers(swapchain->swapchain, buffer_count, width, height, format, flags); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeTarget(IDXGISwapChain4 *iface, ++ const DXGI_MODE_DESC *target_mode_desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeTarget(swapchain->swapchain, target_mode_desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetContainingOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetContainingOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameStatistics(IDXGISwapChain4 *iface, ++ DXGI_FRAME_STATISTICS *stats) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameStatistics(swapchain->swapchain, stats); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetLastPresentCount(IDXGISwapChain4 *iface, ++ UINT *last_present_count) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetLastPresentCount(swapchain->swapchain, last_present_count); ++} ++ ++/* IDXGISwapChain1 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetDesc1(IDXGISwapChain4 *iface, DXGI_SWAP_CHAIN_DESC1 *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetDesc1(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFullscreenDesc(IDXGISwapChain4 *iface, ++ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFullscreenDesc(swapchain->swapchain, desc); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetHwnd(IDXGISwapChain4 *iface, HWND *hwnd) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetHwnd(swapchain->swapchain, hwnd); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCoreWindow(IDXGISwapChain4 *iface, ++ REFIID iid, void **core_window) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCoreWindow(swapchain->swapchain, iid, core_window); ++} ++ ++HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_Present1(IDXGISwapChain4 *iface, ++ UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_Present1(swapchain->swapchain, sync_interval, flags, present_parameters); ++} ++ ++static BOOL STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_IsTemporaryMonoSupported(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRestrictToOutput(IDXGISwapChain4 *iface, IDXGIOutput **output) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRestrictToOutput(swapchain->swapchain, output); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetBackgroundColor(IDXGISwapChain4 *iface, const DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetBackgroundColor(IDXGISwapChain4 *iface, DXGI_RGBA *color) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetBackgroundColor(swapchain->swapchain, color); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetRotation(swapchain->swapchain, rotation); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetRotation(IDXGISwapChain4 *iface, DXGI_MODE_ROTATION *rotation) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetRotation(swapchain->swapchain, rotation); ++} ++ ++/* IDXGISwapChain2 methods */ ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetSourceSize(IDXGISwapChain4 *iface, UINT width, UINT height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetSourceSize(IDXGISwapChain4 *iface, UINT *width, UINT *height) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetSourceSize(swapchain->swapchain, width, height); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMaximumFrameLatency(swapchain->swapchain, max_latency); ++} ++ ++static HANDLE STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetFrameLatencyWaitableObject(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface, ++ const DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetMatrixTransform(IDXGISwapChain4 *iface, ++ DXGI_MATRIX_3X2_F *matrix) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetMatrixTransform(swapchain->swapchain, matrix); ++} ++ ++/* IDXGISwapChain3 methods */ ++ ++static UINT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_GetCurrentBackBufferIndex(IDXGISwapChain4 *iface) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_GetCurrentBackBufferIndex(swapchain->swapchain); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_CheckColorSpaceSupport(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space, UINT *colour_space_support) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_CheckColorSpaceSupport(swapchain->swapchain, colour_space, colour_space_support); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetColorSpace1(IDXGISwapChain4 *iface, ++ DXGI_COLOR_SPACE_TYPE colour_space) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetColorSpace1(swapchain->swapchain, colour_space); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_ResizeBuffers1(IDXGISwapChain4 *iface, ++ UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags, ++ const UINT *node_mask, IUnknown * const *present_queue) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_ResizeBuffers1(swapchain->swapchain, buffer_count, width, height, format, flags, node_mask, present_queue); ++} ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH proxy_swapchain_SetHDRMetaData(IDXGISwapChain4 *iface, ++ DXGI_HDR_METADATA_TYPE type, UINT size, void *metadata) ++{ ++ struct proxy_swapchain *swapchain = proxy_swapchain_from_IDXGISwapChain4(iface); ++ ++ TRACE(".\n"); ++ ++ return IDXGISwapChain4_SetHDRMetaData(swapchain->swapchain, type, size, metadata); ++} ++ ++static const struct IDXGISwapChain4Vtbl proxy_swapchain_vtbl = ++{ ++ /* IUnknown methods */ ++ proxy_swapchain_QueryInterface, ++ proxy_swapchain_AddRef, ++ proxy_swapchain_Release, ++ /* IDXGIObject methods */ ++ proxy_swapchain_SetPrivateData, ++ proxy_swapchain_SetPrivateDataInterface, ++ proxy_swapchain_GetPrivateData, ++ proxy_swapchain_GetParent, ++ /* IDXGIDeviceSubObject methods */ ++ proxy_swapchain_GetDevice, ++ /* IDXGISwapChain methods */ ++ proxy_swapchain_Present, ++ proxy_swapchain_GetBuffer, ++ proxy_swapchain_SetFullscreenState, ++ proxy_swapchain_GetFullscreenState, ++ proxy_swapchain_GetDesc, ++ proxy_swapchain_ResizeBuffers, ++ proxy_swapchain_ResizeTarget, ++ proxy_swapchain_GetContainingOutput, ++ proxy_swapchain_GetFrameStatistics, ++ proxy_swapchain_GetLastPresentCount, ++ /* IDXGISwapChain1 methods */ ++ proxy_swapchain_GetDesc1, ++ proxy_swapchain_GetFullscreenDesc, ++ proxy_swapchain_GetHwnd, ++ proxy_swapchain_GetCoreWindow, ++ proxy_swapchain_Present1, ++ proxy_swapchain_IsTemporaryMonoSupported, ++ proxy_swapchain_GetRestrictToOutput, ++ proxy_swapchain_SetBackgroundColor, ++ proxy_swapchain_GetBackgroundColor, ++ proxy_swapchain_SetRotation, ++ proxy_swapchain_GetRotation, ++ /* IDXGISwapChain2 methods */ ++ proxy_swapchain_SetSourceSize, ++ proxy_swapchain_GetSourceSize, ++ proxy_swapchain_SetMaximumFrameLatency, ++ proxy_swapchain_GetMaximumFrameLatency, ++ proxy_swapchain_GetFrameLatencyWaitableObject, ++ proxy_swapchain_SetMatrixTransform, ++ proxy_swapchain_GetMatrixTransform, ++ /* IDXGISwapChain3 methods */ ++ proxy_swapchain_GetCurrentBackBufferIndex, ++ proxy_swapchain_CheckColorSpaceSupport, ++ proxy_swapchain_SetColorSpace1, ++ proxy_swapchain_ResizeBuffers1, ++ /* IDXGISwapChain4 methods */ ++ proxy_swapchain_SetHDRMetaData, ++}; ++ ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFactory *iface, + IUnknown *device, HWND window, const DXGI_SWAP_CHAIN_DESC1 *desc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc, + IDXGIOutput *output, IDXGISwapChain1 **swapchain) +@@ -299,9 +786,23 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa + + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { ++ IDXGISwapChain4 *swapchain_impl; + hr = IWineDXGISwapChainFactory_create_swapchain(swapchain_factory, +- (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, swapchain); ++ (IDXGIFactory *)iface, window, desc, fullscreen_desc, output, (IDXGISwapChain1 **)&swapchain_impl); + IWineDXGISwapChainFactory_Release(swapchain_factory); ++ if (SUCCEEDED(hr)) ++ { ++ struct proxy_swapchain *obj; ++ ++ obj = calloc(1, sizeof(*obj)); ++ obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; ++ obj->swapchain = swapchain_impl; ++ *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; ++ } ++ else ++ { ++ *swapchain = NULL; ++ } + return hr; + } + +diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c +index 82e5fbf811d..b1153ac4927 100644 +--- a/dlls/dxgi/swapchain.c ++++ b/dlls/dxgi/swapchain.c +@@ -232,7 +232,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_AddRef(IDXGISwapChain1 *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Release(IDXGISwapChain1 *iface) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); + ULONG refcount = InterlockedDecrement(&swapchain->refcount); +@@ -325,7 +325,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac + + /* IDXGISwapChain1 methods */ + +-static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, ++static HRESULT DECLSPEC_HOTPATCH d3d11_swapchain_present(struct d3d11_swapchain *swapchain, + unsigned int sync_interval, unsigned int flags) + { + if (sync_interval > 4) +@@ -697,7 +697,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain1 * + return DXGI_ERROR_INVALID_CALL; + } + +-static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain1 *iface, ++static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present1(IDXGISwapChain1 *iface, + UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) + { + struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain1(iface); +From 0262122fc328410e6b0b410837c35881b3aa85af Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Mon, 19 Apr 2021 15:29:39 +0300 +Subject: [PATCH] winevulkan: Don't hardcode performance frequency. + +For Forza Horizon 4. +--- + dlls/winevulkan/loader.c | 33 +++++++++++++++++++++++++++++++++ + dlls/winevulkan/make_vulkan | 2 +- + 2 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c +index d3ac9f57763..fcb79983114 100644 +--- a/dlls/winevulkan/loader.c ++++ b/dlls/winevulkan/loader.c +@@ -442,6 +442,39 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, + } + } + ++VkResult WINAPI vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) ++{ ++ struct vkGetCalibratedTimestampsEXT_params params; ++ static LARGE_INTEGER freq; ++ VkResult res; ++ uint32_t i; ++ ++ if (!freq.QuadPart) ++ { ++ LARGE_INTEGER temp; ++ ++ QueryPerformanceFrequency(&temp); ++ InterlockedCompareExchange64(&freq.QuadPart, temp.QuadPart, 0); ++ } ++ ++ params.device = device; ++ params.timestampCount = timestampCount; ++ params.pTimestampInfos = pTimestampInfos; ++ params.pTimestamps = pTimestamps; ++ params.pMaxDeviation = pMaxDeviation; ++ res = UNIX_CALL(vkGetCalibratedTimestampsEXT, ¶ms); ++ if (res != VK_SUCCESS) ++ return res; ++ ++ for (i = 0; i < timestampCount; i++) ++ { ++ if (pTimestampInfos[i].timeDomain != VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT) continue; ++ pTimestamps[i] *= freq.QuadPart / 10000000; ++ } ++ ++ return VK_SUCCESS; ++} ++ + static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + { + return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, +diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan +index 516c817715f..a0287c1ff09 100755 +--- a/dlls/winevulkan/make_vulkan ++++ b/dlls/winevulkan/make_vulkan +@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { + + # VK_EXT_calibrated_timestamps + "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, +- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, ++ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, + + # VK_EXT_debug_utils + "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, +From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 7 May 2021 13:44:00 -0500 +Subject: [PATCH] HACK: dxgi: Return a static factory for RE8: Village + +CW-Bug-Id: 18923 +--- + dlls/dxgi/dxgi_main.c | 15 ++++++ + dlls/dxgi/dxgi_private.h | 2 + + dlls/dxgi/factory.c | 99 +++++++++++++++++++++++++++++++++++++++- + 3 files changed, 115 insertions(+), 1 deletion(-) + +diff --git a/dlls/dxgi/dxgi_main.c b/dlls/dxgi/dxgi_main.c +index 83c3f3734a7..941e23b3394 100644 +--- a/dlls/dxgi/dxgi_main.c ++++ b/dlls/dxgi/dxgi_main.c +@@ -56,6 +56,18 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) + return TRUE; + } + ++static BOOL is_re8(void) ++{ ++ static int status = -1; ++ ++ if(status < 0){ ++ const char *sgi = getenv("SteamGameId"); ++ status = sgi && !strcmp(sgi, "1196590"); ++ } ++ ++ return status != 0; ++} ++ + HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID iid, void **factory) + { + TRACE("flags %#x, iid %s, factory %p.\n", flags, debugstr_guid(iid), factory); +@@ -70,6 +82,9 @@ HRESULT WINAPI CreateDXGIFactory1(REFIID iid, void **factory) + { + TRACE("iid %s, factory %p.\n", debugstr_guid(iid), factory); + ++ if(is_re8()) ++ return get_re8_dxgi_factory(iid, factory); ++ + return dxgi_factory_create(iid, factory, TRUE); + } + +diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h +index acba3544e39..9d1b1cde342 100644 +--- a/dlls/dxgi/dxgi_private.h ++++ b/dlls/dxgi/dxgi_private.h +@@ -209,4 +209,6 @@ struct dxgi_surface + HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, + IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++ + #endif /* __WINE_DXGI_PRIVATE_H */ +diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c +index 020faa26a11..ba7d3e78905 100644 +--- a/dlls/dxgi/factory.c ++++ b/dlls/dxgi/factory.c +@@ -1044,6 +1044,8 @@ static const struct IWineDXGIFactoryVtbl dxgi_factory_vtbl = + dxgi_factory_UnregisterAdaptersChangedEvent, + }; + ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl; ++ + struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + { + IWineDXGIFactory *wine_factory; +@@ -1057,7 +1059,8 @@ struct dxgi_factory *unsafe_impl_from_IDXGIFactory(IDXGIFactory *iface) + ERR("Failed to get IWineDXGIFactory interface, hr %#x.\n", hr); + return NULL; + } +- assert(wine_factory->lpVtbl == &dxgi_factory_vtbl); ++ assert(wine_factory->lpVtbl == &dxgi_factory_vtbl || ++ wine_factory->lpVtbl == &re8_factory_vtbl); + factory = CONTAINING_RECORD(wine_factory, struct dxgi_factory, IWineDXGIFactory_iface); + IWineDXGIFactory_Release(wine_factory); + return factory; +@@ -1125,3 +1128,97 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) + + return factory->device_window; + } ++ ++/* re8 calls DXGICreateFactory1 over and over again, which is very expensive in ++ * Wine. instead just cache the first one we create and return that. */ ++static struct dxgi_factory re8_factory; ++ ++static CRITICAL_SECTION re8_factory_lock; ++static CRITICAL_SECTION_DEBUG re8_factory_lock_debug = ++{ ++ 0, 0, &re8_factory_lock, ++ { &re8_factory_lock_debug.ProcessLocksList, &re8_factory_lock_debug.ProcessLocksList }, ++ 0, 0, { (DWORD_PTR)(__FILE__ ": re8_factory_lock") } ++}; ++static CRITICAL_SECTION re8_factory_lock = { &re8_factory_lock_debug, -1, 0, 0, 0, 0 }; ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_AddRef(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static ULONG STDMETHODCALLTYPE DECLSPEC_HOTPATCH re8_factory_Release(IWineDXGIFactory *iface) ++{ ++ TRACE("%p static.\n", iface); ++ return re8_factory.refcount; ++} ++ ++static const struct IWineDXGIFactoryVtbl re8_factory_vtbl = ++{ ++ dxgi_factory_QueryInterface, ++ re8_factory_AddRef, ++ re8_factory_Release, ++ dxgi_factory_SetPrivateData, ++ dxgi_factory_SetPrivateDataInterface, ++ dxgi_factory_GetPrivateData, ++ dxgi_factory_GetParent, ++ dxgi_factory_EnumAdapters, ++ dxgi_factory_MakeWindowAssociation, ++ dxgi_factory_GetWindowAssociation, ++ dxgi_factory_CreateSwapChain, ++ dxgi_factory_CreateSoftwareAdapter, ++ /* IDXGIFactory1 methods */ ++ dxgi_factory_EnumAdapters1, ++ dxgi_factory_IsCurrent, ++ /* IDXGIFactory2 methods */ ++ dxgi_factory_IsWindowedStereoEnabled, ++ dxgi_factory_CreateSwapChainForHwnd, ++ dxgi_factory_CreateSwapChainForCoreWindow, ++ dxgi_factory_GetSharedResourceAdapterLuid, ++ dxgi_factory_RegisterStereoStatusWindow, ++ dxgi_factory_RegisterStereoStatusEvent, ++ dxgi_factory_UnregisterStereoStatus, ++ dxgi_factory_RegisterOcclusionStatusWindow, ++ dxgi_factory_RegisterOcclusionStatusEvent, ++ dxgi_factory_UnregisterOcclusionStatus, ++ dxgi_factory_CreateSwapChainForComposition, ++ /* IDXGIFactory3 methods */ ++ dxgi_factory_GetCreationFlags, ++ /* IDXGIFactory4 methods */ ++ dxgi_factory_EnumAdapterByLuid, ++ dxgi_factory_EnumWarpAdapter, ++ /* IDXIGFactory5 methods */ ++ dxgi_factory_CheckFeatureSupport, ++ /* IDXGIFactory6 methods */ ++ dxgi_factory_EnumAdapterByGpuPreference, ++ /* IDXGIFactory7 methods */ ++ dxgi_factory_RegisterAdaptersChangedEvent, ++ dxgi_factory_UnregisterAdaptersChangedEvent, ++}; ++ ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) ++{ ++ HRESULT hr; ++ ++ EnterCriticalSection(&re8_factory_lock); ++ ++ if(re8_factory.refcount == 0){ ++ if (FAILED(hr = dxgi_factory_init(&re8_factory, TRUE))) ++ { ++ WARN("Failed to initialize factory, hr %#x.\n", hr); ++ LeaveCriticalSection(&re8_factory_lock); ++ return hr; ++ } ++ ++ re8_factory.IWineDXGIFactory_iface.lpVtbl = &re8_factory_vtbl; ++ ++ TRACE("Created factory %p.\n", &re8_factory); ++ } ++ ++ LeaveCriticalSection(&re8_factory_lock); ++ ++ hr = IWineDXGIFactory_QueryInterface(&re8_factory.IWineDXGIFactory_iface, riid, factory); ++ ++ return hr; ++} + + +From 43c3113aa696898ec5758118b92f8c2a5fb61714 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 30 Jul 2021 21:01:13 +0300 +Subject: [PATCH] kernelbase: HACK: Force CEF software rendering for + UplayWebCore. + +(To be revisited once builtin d3dcompiler is added). +--- + dlls/kernelbase/process.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 33197563cd8..9bcb377a62e 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -33,6 +33,7 @@ + #include "kernelbase.h" + #include "wine/debug.h" + #include "wine/condrv.h" ++#include "wine/heap.h" + + WINE_DEFAULT_DEBUG_CHANNEL(process); + +@@ -517,7 +518,31 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + } + else + { +- if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; ++ static const WCHAR *opt = L" --use-gl=swiftshader"; ++ WCHAR *cmdline_new = NULL; ++ ++ if (cmd_line && wcsstr( cmd_line, L"UplayWebCore.exe" )) ++ { ++ FIXME( "HACK: appending %s to command line %s.\n", debugstr_w(opt), debugstr_w(cmd_line) ); ++ ++ cmdline_new = heap_alloc( sizeof(WCHAR) * (lstrlenW(cmd_line) + lstrlenW(opt) + 1) ); ++ lstrcpyW(cmdline_new, cmd_line); ++ lstrcatW(cmdline_new, opt); ++ } ++ ++ tidy_cmdline = get_file_name( cmdline_new ? cmdline_new : cmd_line, name, ARRAY_SIZE(name) ); ++ ++ if (!tidy_cmdline) ++ { ++ heap_free( cmdline_new ); ++ return FALSE; ++ } ++ ++ if (cmdline_new) ++ { ++ if (cmdline_new == tidy_cmdline) cmd_line = NULL; ++ else heap_free( cmdline_new ); ++ } + app_name = name; + } + +From a9ec37d83082b1623aea4bc5933f77f4c8a356cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:47 +0200 +Subject: [PATCH] msvcrt: Check for ERMS support and use rep stosb for large + memset calls. + +--- + dlls/msvcrt/math.c | 13 +++++++++ + dlls/msvcrt/msvcrt.h | 1 + + dlls/msvcrt/string.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 78 insertions(+) + +diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c +index d6b0d9422e7..ed763f02589 100644 +--- a/dlls/msvcrt/math.c ++++ b/dlls/msvcrt/math.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include "msvcrt.h" + #include "winternl.h" +@@ -64,6 +65,7 @@ typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + + static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + ++BOOL erms_supported; + BOOL sse2_supported; + static BOOL sse2_enabled; + +@@ -71,6 +73,17 @@ static const struct unix_funcs *unix_funcs; + + void msvcrt_init_math( void *module ) + { ++#if defined(__i386__) || defined(__x86_64__) ++ int regs[4]; ++ ++ __cpuid(regs, 0); ++ if (regs[0] >= 7) ++ { ++ __cpuidex(regs, 7, 0); ++ erms_supported = ((regs[1] >> 9) & 1); ++ } ++#endif ++ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); + #if _MSVCR_VER <=71 + sse2_enabled = FALSE; +diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h +index c84e0a0a638..69c52616e3f 100644 +--- a/dlls/msvcrt/msvcrt.h ++++ b/dlls/msvcrt/msvcrt.h +@@ -32,6 +32,7 @@ + #undef strncpy + #undef wcsncpy + ++extern BOOL erms_supported; + extern BOOL sse2_supported; + + #define DBL80_MAX_10_EXP 4932 +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 70cca8d18ee..3daa0d483d0 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2722,6 +2722,13 @@ __ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_CLEANUP + "ret" ) + ++#undef MEMMOVE_INIT ++#undef MEMMOVE_CLEANUP ++#undef DEST_REG ++#undef SRC_REG ++#undef LEN_REG ++#undef TMP_REG ++ + #endif + + /********************************************************************* +@@ -2845,6 +2852,56 @@ void * __cdecl memcpy(void *dst, const void *src, size_t n) + return memmove(dst, src, n); + } + ++#if defined(__i386__) || defined(__x86_64__) ++ ++#ifdef __i386__ ++#define DEST_REG "%edi" ++#define LEN_REG "%ecx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movl " DEST_REG ", %edx\n\t" \ ++ "movl 4(%esp), " DEST_REG "\n\t" \ ++ "movl 8(%esp), " VAL_REG "\n\t" \ ++ "movl 12(%esp), " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movl %edx, " DEST_REG "\n\t" \ ++ "ret" ++ ++#else ++ ++#define DEST_REG "%rdi" ++#define LEN_REG "%rcx" ++#define VAL_REG "%eax" ++ ++#define MEMSET_INIT \ ++ "movq " DEST_REG ", %r9\n\t" \ ++ "movq %rcx, " DEST_REG "\n\t" \ ++ "movl %edx, " VAL_REG "\n\t" \ ++ "movq %r8, " LEN_REG "\n\t" ++ ++#define MEMSET_RET \ ++ "movq %r9, " DEST_REG "\n\t" \ ++ "ret" ++ ++#endif ++ ++void __cdecl erms_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( erms_memset_aligned_32, ++ MEMSET_INIT ++ "rep\n\t" ++ "stosb\n\t" ++ MEMSET_RET ) ++ ++#undef MEMSET_INIT ++#undef MEMSET_RET ++#undef DEST_REG ++#undef LEN_REG ++#undef VAL_REG ++ ++#endif ++ + static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) + { + while (n >= 32) +@@ -2880,6 +2937,13 @@ void *__cdecl memset(void *dst, int c, size_t n) + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; ++#if defined(__i386__) || defined(__x86_64__) ++ if (n >= 2048 && erms_supported) ++ { ++ erms_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + memset_aligned_32(d + a, v, n); + return dst; + } +From 454d37da51ed35ce16d73ba162c4fad3de7f93b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 14 Sep 2021 14:16:48 +0200 +Subject: [PATCH] msvcrt: Add an SSE2 memset_aligned_32 implementation. + +--- + dlls/msvcrt/string.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 3daa0d483d0..74409a6fb76 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2894,6 +2894,27 @@ __ASM_GLOBAL_FUNC( erms_memset_aligned_32, + "stosb\n\t" + MEMSET_RET ) + ++void __cdecl sse2_memset_aligned_32(unsigned char *d, unsigned int c, size_t n); ++__ASM_GLOBAL_FUNC( sse2_memset_aligned_32, ++ MEMSET_INIT ++ "movd " VAL_REG ", %xmm0\n\t" ++ "pshufd $0, %xmm0, %xmm0\n\t" ++ "test $0x20, " LEN_REG "\n\t" ++ "je 1f\n\t" ++ "sub $0x20, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "je 2f\n\t" ++ "1:\n\t" ++ "sub $0x40, " LEN_REG "\n\t" ++ "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "ja 1b\n\t" ++ "2:\n\t" ++ MEMSET_RET ) ++ + #undef MEMSET_INIT + #undef MEMSET_RET + #undef DEST_REG +@@ -2943,9 +2964,21 @@ void *__cdecl memset(void *dst, int c, size_t n) + erms_memset_aligned_32(d + a, v, n); + return dst; + } ++#ifdef __x86_64__ ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++#else ++ if (sse2_supported) ++ { ++ sse2_memset_aligned_32(d + a, v, n); ++ return dst; ++ } ++#endif + #endif ++#ifndef __x86_64__ + memset_aligned_32(d + a, v, n); + return dst; ++#endif + } + if (n >= 8) + { +From 6d574d1275635d1fda1177707a8f3146249b52ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Wed, 13 Oct 2021 15:19:37 +0200 +Subject: [PATCH] msvcrt: Write memory forward in memset. + +Instead of going backward, which may break the Linux kernel page fault +optimizations. +--- + dlls/msvcrt/string.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c +index 74409a6fb76..63a11c27e03 100644 +--- a/dlls/msvcrt/string.c ++++ b/dlls/msvcrt/string.c +@@ -2901,16 +2901,18 @@ __ASM_GLOBAL_FUNC( sse2_memset_aligned_32, + "pshufd $0, %xmm0, %xmm0\n\t" + "test $0x20, " LEN_REG "\n\t" + "je 1f\n\t" ++ "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "je 2f\n\t" + "1:\n\t" ++ "add $0x40, " DEST_REG "\n\t" + "sub $0x40, " LEN_REG "\n\t" +- "movdqa %xmm0, 0x00(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x10(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x20(" DEST_REG ", " LEN_REG ")\n\t" +- "movdqa %xmm0, 0x30(" DEST_REG ", " LEN_REG ")\n\t" ++ "movdqa %xmm0, -0x40(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x30(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x20(" DEST_REG ")\n\t" ++ "movdqa %xmm0, -0x10(" DEST_REG ")\n\t" + "ja 1b\n\t" + "2:\n\t" + MEMSET_RET ) +From 1ace788f817b0175e63d02732c0c11269d19ae0a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 16 Sep 2021 18:01:42 +0300 +Subject: [PATCH] kernelbase: Return an error from + InitializeProcessForWsWatch() stub. + +CW-Bug-ID: #19445 + +For DeathLoop. +--- + dlls/kernelbase/debug.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c +index cbc53e22ac1..60b03c57137 100644 +--- a/dlls/kernelbase/debug.c ++++ b/dlls/kernelbase/debug.c +@@ -1525,7 +1525,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INF + BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) + { + FIXME( "(process=%p): stub\n", process ); +- return TRUE; ++ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); ++ return FALSE; + } + + +From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Fri, 4 Jun 2021 10:24:10 +0200 +Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry + ~Mhz. + +In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor + +Squashed with patches from: + +* Arkadiusz Hiler + +Check if the kernel trusts TSC before using it for Qpc. + +Even if the bits are claiming that TSC meets our requirements the +hardware implementation may still be broken. + +The Linux kernel does a lot of quality testing before deciding to use as +the clock source. If it (or the user, through an override) does not trust +the TSC we should not trust it either. + +* Joshua Ashton + +Some games such as Horizon Zero Dawn use this registry value to +correlate values from rtdsc to real time. + +Testing across a few devices, is seems like Windows always returns the +TSC frequency in this entry, not the current/maximum frequency of the +processor. + +Returning the nominal/maximum cpu frequency here causes the game to run +in slow motion as it may not match the tsc frequency of the processor. + +Ideally we'd not have to measure this and the kernel would return +tsc_khz to userspace, but this is a good enough stop-gap until +https://lkml.org/lkml/2020/12/31/72 or something similar is merged. + +CW-Bug-Id: #18918 +CW-Bug-Id: #18958 +--- + programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- + 1 file changed, 170 insertions(+), 5 deletions(-) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index c05ce580298..731242c75eb 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -82,6 +82,8 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wineboot); + ++#define TICKSPERSEC 10000000 ++ + extern BOOL shutdown_close_windows( BOOL force ); + extern BOOL shutdown_all_desktops( BOOL force ); + extern void kill_processes( BOOL kill_desktop ); +@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); + } + ++static UINT64 read_tsc_frequency( BOOL has_rdtscp ) ++{ ++ UINT64 freq = 0; ++ ++/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, ++ fix it properly and test it on real Intel hardware */ ++ ++#if 0 ++ int regs[4], cpuid_level, tmp; ++ UINT64 denom, numer; ++ ++ __cpuid( regs, 0 ); ++ tmp = regs[2]; ++ regs[2] = regs[3]; ++ regs[3] = tmp; ++ ++ /* only available on some intel CPUs */ ++ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; ++ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; ++ else ++ { ++ __cpuid( regs, 0x15 ); ++ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; ++ else ++ { ++ if ((freq = regs[2])) freq = freq * numer / denom; ++ else if (cpuid_level >= 0x16) ++ { ++ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ ++ freq = regs[0] * (UINT64)1000000; ++ } ++ else freq = 0; ++ } ++ ++ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); ++ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); ++ } ++#endif ++ ++ if (freq == 0) ++ { ++ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; ++ unsigned int aux; ++ UINT retries = 50; ++ int regs[4]; ++ ++ do ++ { ++ if (has_rdtscp) ++ { ++ tsc0 = __rdtscp( &aux ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtscp( &aux ); ++ Sleep( 1 ); ++ tsc2 = __rdtscp( &aux ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtscp( &aux ); ++ } ++ else ++ { ++ tsc0 = __rdtsc(); __cpuid( regs, 0 ); ++ time0 = RtlGetSystemTimePrecise(); ++ tsc1 = __rdtsc(); __cpuid( regs, 0 ); ++ Sleep(1); ++ tsc2 = __rdtsc(); __cpuid( regs, 0 ); ++ time1 = RtlGetSystemTimePrecise(); ++ tsc3 = __rdtsc(); __cpuid( regs, 0 ); ++ } ++ ++ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); ++ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); ++ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); ++ } ++ while (error > 100 && --retries); ++ ++ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); ++ else ++ { ++ freq = (freq0 + freq1) / 2; ++ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); ++ } ++ } ++ ++ return freq; ++} ++ ++static BOOL is_tsc_trusted_by_the_kernel(void) ++{ ++ char buf[4] = {}; ++ DWORD num_read; ++ HANDLE handle; ++ BOOL ret = TRUE; ++ ++ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", ++ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); ++ if (handle == INVALID_HANDLE_VALUE) return TRUE; ++ ++ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) ++ ret = FALSE; ++ ++ CloseHandle( handle ); ++ return ret; ++} ++ ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ BOOL has_rdtscp = FALSE; ++ int regs[4]; ++ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++ ++ if (!is_tsc_trusted_by_the_kernel()) ++ { ++ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); ++ return; ++ } ++ ++ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) ++ { ++ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); ++ return; ++ } ++ ++ __cpuid( regs, 0x80000000 ); ++ if (regs[0] < 0x80000007) ++ { ++ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for invariant tsc bit */ ++ __cpuid( regs, 0x80000007 ); ++ if (!(regs[3] & (1 << 8))) ++ { ++ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); ++ return; ++ } ++ ++ /* check for rdtscp support bit */ ++ __cpuid( regs, 0x80000001 ); ++ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; ++ ++ *tsc_frequency = read_tsc_frequency( has_rdtscp ); ++} ++ + #else + + static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) + { + } + ++static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) ++{ ++ data->QpcBypassEnabled = 0; ++ data->QpcFrequency = TICKSPERSEC; ++ data->QpcShift = 0; ++ data->QpcBias = 0; ++ *tsc_frequency = 0; ++} ++ + #endif + +-static void create_user_shared_data(void) ++static void create_user_shared_data( UINT64 *tsc_frequency ) + { + struct _KUSER_SHARED_DATA *data; + RTL_OSVERSIONINFOEXW version; +@@ -368,6 +528,7 @@ static void create_user_shared_data(void) + data->ActiveGroupCount = 1; + + initialize_xstate_features( data ); ++ initialize_qpc_features( data, tsc_frequency ); + + UnmapViewOfFile( data ); + } +@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) + } + + /* create the volatile hardware registry keys */ +-static void create_hardware_registry_keys(void) ++static void create_hardware_registry_keys( UINT64 tsc_frequency ) + { + unsigned int i; + HKEY hkey, system_key, cpu_key, fpu_key; +@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) + if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, + KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { ++ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ ++ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; ++ + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); + set_reg_value( hkey, L"Identifier", id ); + /* TODO: report ARM properly */ + RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, + strlen( (char *)name_buffer ) + 1 ); + set_reg_value( hkey, L"VendorIdentifier", vendorid ); +- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); ++ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); + RegCloseKey( hkey ); + } + if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && +@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) + BOOL end_session, force, init, kill, restart, shutdown, update; + HANDLE event; + OBJECT_ATTRIBUTES attr; ++ UINT64 tsc_frequency = 0; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); + BOOL is_wow64; + +@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) + + ResetEvent( event ); /* in case this is a restart */ + +- create_user_shared_data(); +- create_hardware_registry_keys(); ++ create_user_shared_data( &tsc_frequency ); ++ create_hardware_registry_keys( tsc_frequency ); + create_dynamic_registry_keys(); + create_environment_registry_keys(); + create_computer_name_keys(); + + +From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 5 Jan 2022 13:31:14 +0300 +Subject: [PATCH] audioses: Add stub dll. + +CW-Bug-Id: #19918 +--- + configure.ac | 1 + + dlls/audioses/Makefile.in | 1 + + dlls/audioses/audioses.spec | 11 +++++++++++ + 3 files changed, 13 insertions(+) + create mode 100644 dlls/audioses/Makefile.in + create mode 100644 dlls/audioses/audioses.spec + +diff --git a/configure.ac b/configure.ac +index f37f4e02324..95f9a35bf7f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2625,6 +2625,7 @@ WINE_CONFIG_MAKEFILE(dlls/atl90) + WINE_CONFIG_MAKEFILE(dlls/atlthunk) + WINE_CONFIG_MAKEFILE(dlls/atlthunk/tests) + WINE_CONFIG_MAKEFILE(dlls/atmlib) ++WINE_CONFIG_MAKEFILE(dlls/audioses) + WINE_CONFIG_MAKEFILE(dlls/authz) + WINE_CONFIG_MAKEFILE(dlls/avicap32) + WINE_CONFIG_MAKEFILE(dlls/avifil32) +diff --git a/dlls/audioses/Makefile.in b/dlls/audioses/Makefile.in +new file mode 100644 +index 00000000000..370949ea4fe +--- /dev/null ++++ b/dlls/audioses/Makefile.in +@@ -0,0 +1 @@ ++MODULE = audioses.dll +diff --git a/dlls/audioses/audioses.spec b/dlls/audioses/audioses.spec +new file mode 100644 +index 00000000000..a1884e53243 +--- /dev/null ++++ b/dlls/audioses/audioses.spec +@@ -0,0 +1,11 @@ ++# @ stub AUDIOSES_1 ++# @ stub AUDIOSES_2 ++# @ stub AUDIOSES_3 ++# @ stub AUDIOSES_4 ++# @ stub AUDIOSES_5 ++# @ stub DllCanUnloadNow ++# @ stub AUDIOSES_7 ++# @ stub DllGetActivationFactory ++# @ stub DllGetClassObject ++# @ stub DllRegisterServer ++# @ stub DllUnregisterServer +From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Thu, 19 Dec 2019 09:12:17 -0600 +Subject: [PATCH] winex11.drv: Remove nvidia hack workaround + +This breaks things for users who legitimately have only one resolution. +--- + dlls/winex11.drv/xrandr.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c +index 0a080ea6be8..667013df5ac 100644 +--- a/dlls/winex11.drv/xrandr.c ++++ b/dlls/winex11.drv/xrandr.c +@@ -28,9 +28,6 @@ + #include "wine/debug.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xrandr); +-#ifdef HAVE_XRRGETPROVIDERRESOURCES +-WINE_DECLARE_DEBUG_CHANNEL(winediag); +-#endif + + #ifdef SONAME_LIBXRANDR + +@@ -378,7 +375,6 @@ static BOOL is_broken_driver(void) + XRRScreenResources *screen_resources; + XRROutputInfo *output_info; + XRRModeInfo *first_mode; +- INT major, event, error; + INT output_idx, i, j; + BOOL only_one_mode; + +@@ -429,15 +425,6 @@ static BOOL is_broken_driver(void) + + if (!only_one_mode) + continue; +- +- /* Check if it is NVIDIA proprietary driver */ +- if (XQueryExtension( gdi_display, "NV-CONTROL", &major, &event, &error )) +- { +- ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. " +- "Please consider using the Nouveau driver instead.\n"); +- pXRRFreeScreenResources( screen_resources ); +- return TRUE; +- } + } + pXRRFreeScreenResources( screen_resources ); + return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/proton-tkg-staging.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/proton-tkg-staging.patch index 69ff910d5..6583ae153 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/proton-tkg-staging.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/proton-tkg-staging.patch @@ -149,7 +149,7 @@ index c6e3a2ca597..fd919295c5a 100644 - if (!tm) return E_UNEXPECTED; + if (FAILED(TF_GetThreadMgr(&tm))) return E_UNEXPECTED; - actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); + actsvr = malloc(sizeof(ActivatedTextService)); - if (!actsvr) return E_OUTOFMEMORY; + if (!actsvr) goto fail; @@ -160,7 +160,7 @@ index c6e3a2ca597..fd919295c5a 100644 - if (!actsvr->tid) - { -- HeapFree(GetProcessHeap(),0,actsvr); +- free(actsvr); - return E_OUTOFMEMORY; - } + if (!actsvr->tid) goto fail; @@ -174,11 +174,11 @@ index c6e3a2ca597..fd919295c5a 100644 - activate_given_ts(actsvr, tm); + activate_given_ts(actsvr, (ITfThreadMgrEx *)tm); - entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); + entry = malloc(sizeof(AtsEntry)); - - if (!entry) - { -- HeapFree(GetProcessHeap(),0,actsvr); +- free(actsvr); - return E_OUTOFMEMORY; - } + if (!entry) goto fail; @@ -191,7 +191,7 @@ index c6e3a2ca597..fd919295c5a 100644 + +fail: + ITfThreadMgr_Release(tm); -+ HeapFree(GetProcessHeap(), 0, actsvr); ++ free(actsvr); + return E_OUTOFMEMORY; } @@ -237,9 +237,9 @@ index 584bb1044ed..ace2bee23d9 100644 #define COOKIE_MAGIC_UIELEMENTSINK 0x00a0 #define COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK 0x00b0 --extern DWORD tlsIndex DECLSPEC_HIDDEN; - extern TfClientId processId DECLSPEC_HIDDEN; - extern ITfCompartmentMgr *globalCompartmentMgr DECLSPEC_HIDDEN; +-extern DWORD tlsIndex; + extern TfClientId processId; + extern ITfCompartmentMgr *globalCompartmentMgr; diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c index 2c208fbc04f..2119ea2193b 100644 @@ -376,7 +376,7 @@ index 2c208fbc04f..2119ea2193b 100644 return S_OK; - } - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgr)); + This = calloc(1, sizeof(ThreadMgr)); if (This == NULL) @@ -1359,7 +1408,6 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; @@ -652,10 +652,10 @@ index 69c4a2f3902..1a3e88a0d29 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -202,7 +202,6 @@ HRESULT IKsPrivatePropertySetImpl_Create(REFIID riid, void **ppv) DECLSPEC_HIDDE - HRESULT DSOUND_Create(REFIID riid, void **ppv) DECLSPEC_HIDDEN; - HRESULT DSOUND_Create8(REFIID riid, void **ppv) DECLSPEC_HIDDEN; - HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8) DECLSPEC_HIDDEN; --void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT DSOUND_Create(REFIID riid, void **ppv); + HRESULT DSOUND_Create8(REFIID riid, void **ppv); + HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8); +-void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device); /* primary.c */ @@ -759,102 +759,6 @@ index 852ec51b7ff..fdbbc00cd33 100644 device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); DSOUND_ParseSpeakerConfig(device); } else if (mixwfe->Format.nChannels > device->num_speakers) { -From 749bf5fcd5f41a39301428a7fccd1e4febcd6f90 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= -Date: Fri, 13 Dec 2019 15:54:28 +0200 -Subject: [PATCH] dwmapi: Improve DwmGetWindowAttribute stub. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Gabriel Ivăncescu ---- - dlls/dwmapi/dwmapi_main.c | 26 ++++++++++++++++++++++++-- - 1 file changed, 24 insertions(+), 2 deletions(-) - -diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c -index 6378a091f0b..e976fda77f2 100644 ---- a/dlls/dwmapi/dwmapi_main.c -+++ b/dlls/dwmapi/dwmapi_main.c -@@ -205,9 +205,31 @@ BOOL WINAPI DwmDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, - */ - HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attribute, DWORD size) - { -- FIXME("(%p %ld %p %ld) stub\n", hwnd, attribute, pv_attribute, size); -+ if (!hwnd) return E_HANDLE; -+ if (!pv_attribute) return E_INVALIDARG; - -- return E_NOTIMPL; -+ switch (attribute) -+ { -+ case DWMWA_NCRENDERING_ENABLED: -+ if (size < sizeof(BOOL)) return E_INVALIDARG; -+ -+ WARN("DWMWA_NCRENDERING_ENABLED: always returning FALSE.\n"); -+ *(BOOL*)(pv_attribute) = FALSE; -+ break; -+ -+ case DWMWA_CLOAKED: -+ if (size < sizeof(DWORD)) return E_INVALIDARG; -+ -+ WARN("DWMWA_CLOAKED: always returning 0.\n"); -+ *(DWORD*)(pv_attribute) = 0; -+ break; -+ -+ default: -+ FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", attribute, size, hwnd); -+ return E_INVALIDARG; -+ } -+ -+ return S_OK; - } - - /********************************************************************** -From 1a2e600a9c35a9558b32e7986668d7dc81c8b5ae Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= -Date: Fri, 13 Dec 2019 15:54:30 +0200 -Subject: [PATCH] dwmapi: Add partial implementation of - DWMWA_EXTENDED_FRAME_BOUNDS. -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Gabriel Ivăncescu ---- - dlls/dwmapi/Makefile.in | 1 + - dlls/dwmapi/dwmapi_main.c | 7 +++++++ - dlls/dwmapi/tests/dwmapi.c | 14 ++++++++++++++ - 3 files changed, 22 insertions(+) - -diff --git a/dlls/dwmapi/Makefile.in b/dlls/dwmapi/Makefile.in -index 3a3691326f8..d273a22c8f3 100644 ---- a/dlls/dwmapi/Makefile.in -+++ b/dlls/dwmapi/Makefile.in -@@ -1,5 +1,6 @@ - MODULE = dwmapi.dll - IMPORTLIB = dwmapi -+IMPORTS = user32 - - EXTRADLLFLAGS = -Wb,--prefer-native - -diff --git a/dlls/dwmapi/dwmapi_main.c b/dlls/dwmapi/dwmapi_main.c -index e976fda77f2..212c88c5a02 100644 ---- a/dlls/dwmapi/dwmapi_main.c -+++ b/dlls/dwmapi/dwmapi_main.c -@@ -217,6 +217,13 @@ HRESULT WINAPI DwmGetWindowAttribute(HWND hwnd, DWORD attribute, PVOID pv_attrib - *(BOOL*)(pv_attribute) = FALSE; - break; - -+ case DWMWA_EXTENDED_FRAME_BOUNDS: -+ if (size < sizeof(RECT)) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); -+ -+ WARN("DWMWA_EXTENDED_FRAME_BOUNDS: returning window rect.\n"); -+ GetWindowRect(hwnd, pv_attribute); -+ break; -+ - case DWMWA_CLOAKED: - if (size < sizeof(DWORD)) return E_INVALIDARG; - From b739d48093cce805b7b4f48fdbd9d0bb62bc8013 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Mon, 13 Apr 2020 16:25:47 -0700 @@ -1291,7 +1195,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1181,7 +1181,7 @@ static WCHAR *get_bios_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_bios_manufacturer( const char *buf, UINT len ) { - WCHAR *ret = get_bios_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, vendor), buf, len ); - if (!ret) return wcsdup( L"The Wine Project" ); + if (!ret) return wcsdup( L"The Proton Project" ); return ret; @@ -1300,7 +1204,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1227,7 +1227,7 @@ static WCHAR *get_bios_releasedate( const char *buf, UINT len ) static WCHAR *get_bios_smbiosbiosversion( const char *buf, UINT len ) { - WCHAR *ret = get_bios_string( 2, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_BIOS, offsetof(struct smbios_bios, version), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1318,7 +1222,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -1641,14 +1641,14 @@ static WCHAR *get_compsysproduct_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_compsysproduct_name( const char *buf, UINT len ) { - WCHAR *ret = get_compsysproduct_string( 2, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, product), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1326,21 +1230,21 @@ index 43268221936..f39d6f0cd7b 100644 static WCHAR *get_compsysproduct_vendor( const char *buf, UINT len ) { - WCHAR *ret = get_compsysproduct_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_SYSTEM, offsetof(struct smbios_system, vendor), buf, len ); - if (!ret) return wcsdup( L"The Wine Project" ); + if (!ret) return wcsdup( L"The Proton Project" ); return ret; } -@@ -2652,7 +2652,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e +@@ -2894,7 +2894,7 @@ static enum fill_status fill_networkadapter( struct table *table, const struct e rec->index = aa->u.s.IfIndex; rec->interface_index = aa->u.s.IfIndex; rec->mac_address = get_mac_address( aa->PhysicalAddress, aa->PhysicalAddressLength ); - rec->manufacturer = L"The Wine Project"; + rec->manufacturer = L"The Proton Project"; + rec->netconnection_id = NULL; /* FIXME Windows seems to fill this when it's connected and in use */ rec->name = wcsdup( aa->FriendlyName ); - rec->netconnection_status = get_connection_status( aa->OperStatus ); - rec->physicaladapter = physical; + rec->netenabled = connection_status ? -1 : 0; @@ -3475,7 +3475,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct rec->lastbootuptime = get_lastbootuptime(); rec->localdatetime = get_localdatetime(); @@ -1353,7 +1257,7 @@ index 43268221936..f39d6f0cd7b 100644 @@ -3740,7 +3740,7 @@ static WCHAR *get_systemenclosure_string( BYTE id, const char *buf, UINT len ) static WCHAR *get_systemenclosure_manufacturer( const char *buf, UINT len ) { - WCHAR *ret = get_systemenclosure_string( 1, buf, len ); + WCHAR *ret = get_smbios_string( SMBIOS_TYPE_CHASSIS, offsetof(struct smbios_chassis, vendor), buf, len ); - if (!ret) return wcsdup( L"Wine" ); + if (!ret) return wcsdup( L"Proton" ); return ret; @@ -1503,7 +1407,7 @@ index 00000000000..e50ed37f700 + +EXTRADLLFLAGS = -mwindows -mno-cygwin + -+C_SRCS = \ ++SOURCES = \ + main.c diff --git a/programs/dotnetfx35/main.c b/programs/dotnetfx35/main.c new file mode 100644 @@ -1558,9 +1462,9 @@ index af2094a96f0..9c98209d43d 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -76,7 +76,7 @@ BOOL use_primary_selection = FALSE; + BOOL use_take_focus = TRUE; + BOOL use_primary_selection = FALSE; BOOL use_system_cursors = TRUE; - BOOL show_systray = TRUE; - BOOL grab_pointer = TRUE; -BOOL grab_fullscreen = FALSE; +BOOL grab_fullscreen = TRUE; int keyboard_layout = -1; @@ -1622,8 +1526,9 @@ index a4a10bc8e93..551ef146cd7 100644 -EXTRADLLFLAGS = -Wb,--prefer-native - - RC_SRCS = version.rc - + SOURCES = \ + version.rc \ + vulkan.c From 07461415f4399934a3c03c8dd6e146167bd67dfd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 25 Mar 2021 17:53:37 +0300 @@ -1986,8 +1891,8 @@ index dca4ffb8647..560e45e965a 100644 + || !(arch_str = string_from_processor_arch(package_id->processorArchitecture))) + return ERROR_INVALID_PARAMETER; + -+ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.u.s.Major, -+ package_id->version.u.s.Minor, package_id->version.u.s.Build, package_id->version.u.s.Revision); ++ swprintf(ver_str, ARRAY_SIZE(ver_str), L"%u.%u.%u.%u", package_id->version.Major, ++ package_id->version.Minor, package_id->version.Build, package_id->version.Revision); + have_length = *length; + *length = lstrlenW(package_id->name) + 1 + lstrlenW(ver_str) + 1 + lstrlenW(arch_str) + 1 + + lstrlenW(package_id->resourceId) + 1 + lstrlenW(package_id->publisherId) + 1; @@ -2160,6 +2065,68 @@ index be59bc70f5f..c73cb8d26ef 100644 -- 2.30.2 +From 240556e2b8cb94fc9cc85949b7e043f392b1802a Mon Sep 17 00:00:00 2001 +From: Etaash Mathamsetty +Date: Tue, 22 Aug 2023 10:35:33 -0400 +Subject: [PATCH] kernelbase: Add GetPackagePathByFullName stub. + +--- + dlls/kernel32/kernel32.spec | 1 + + dlls/kernelbase/kernelbase.spec | 2 +- + dlls/kernelbase/version.c | 13 +++++++++++++ + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec +index eb70a8763b50..52a17bd7a8c2 100644 +--- a/dlls/kernel32/kernel32.spec ++++ b/dlls/kernel32/kernel32.spec +@@ -775,6 +775,7 @@ + @ stdcall GetPackageFamilyName(long ptr ptr) kernelbase.GetPackageFamilyName + @ stdcall GetPackageFullName(long ptr ptr) kernelbase.GetPackageFullName + @ stdcall -import GetPackagePath(ptr long ptr ptr) ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) kernelbase.GetPackagePathByFullName + @ stdcall -import GetPhysicallyInstalledSystemMemory(ptr) + @ stdcall -import GetPriorityClass(long) + @ stdcall GetPrivateProfileIntA(str str long str) +diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec +index 5d5256eb65fc..104dd99d619a 100644 +--- a/dlls/kernelbase/kernelbase.spec ++++ b/dlls/kernelbase/kernelbase.spec +@@ -631,7 +631,7 @@ + # @ stub GetPackageInstallTime + # @ stub GetPackageOSMaxVersionTested + @ stdcall GetPackagePath(ptr long ptr ptr) +-# @ stub GetPackagePathByFullName ++@ stdcall GetPackagePathByFullName(wstr ptr wstr) + # @ stub GetPackagePathOnVolume + # @ stub GetPackageProperty + # @ stub GetPackagePropertyString +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index d8db79b1263a..4631ac8d0bcf 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -1618,6 +1618,19 @@ LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_nam + return ERROR_SUCCESS; + } + ++/*********************************************************************** ++ * GetPackagePathByFullName (kernelbase.@) ++ */ ++LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) ++{ ++ if (!len || !name) ++ return ERROR_INVALID_PARAMETER; ++ ++ FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); ++ ++ return APPMODEL_ERROR_NO_PACKAGE; ++} ++ + static const struct + { + UINT32 code; + + From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 12 Mar 2021 23:58:39 +0300 @@ -2308,7 +2275,7 @@ index af18bdd2c32..020faa26a11 100644 + TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); + + if (!refcount) -+ heap_free(swapchain); ++ free(swapchain); + + return refcount; +} @@ -2757,7 +2724,7 @@ index af18bdd2c32..020faa26a11 100644 + { + struct proxy_swapchain *obj; + -+ obj = heap_alloc_zero(sizeof(*obj)); ++ obj = calloc(1, sizeof(*obj)); + obj->IDXGISwapChain4_iface.lpVtbl = &proxy_swapchain_vtbl; + obj->swapchain = swapchain_impl; + *swapchain = (IDXGISwapChain1 *)&obj->IDXGISwapChain4_iface; @@ -2852,70 +2819,29 @@ index d3ac9f57763..fcb79983114 100644 + return VK_SUCCESS; +} + - static BOOL WINAPI call_vulkan_debug_report_callback( struct wine_vk_debug_report_params *params, ULONG size ) + static NTSTATUS WINAPI call_vulkan_debug_report_callback( void *args, ULONG size ) { - return params->user_callback(params->flags, params->object_type, params->object_handle, params->location, + struct wine_vk_debug_report_params *params = args; diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 516c817715f..a0287c1ff09 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan -@@ -254,7 +254,7 @@ FUNCTION_OVERRIDES = { - - # VK_EXT_calibrated_timestamps - "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, -- "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, -+ "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, - - # VK_EXT_debug_utils - "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, -From fec2ecbd2dfd3c357f63c8f7bd9383b7a10d4553 Mon Sep 17 00:00:00 2001 -From: Esme Povirk -Date: Thu, 22 Apr 2021 14:35:40 -0500 -Subject: [PATCH] HACK: mscoree: For M&B2:Bannerlord, redirect ManagedStarter - loads to Bannerlord.exe - -For M&B2:Bannerlord. ---- - dlls/mscoree/metahost.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - -diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c -index d9b599fadc7..25acdced21e 100644 ---- a/dlls/mscoree/metahost.c -+++ b/dlls/mscoree/metahost.c -@@ -1742,6 +1742,32 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname - } - } - -+ if (!strcmp(assemblyname, "ManagedStarter")) -+ { -+ /* HACK for Mount & Blade II: Bannerlord -+ * -+ * The launcher executable uses an AssemblyResolve event handler -+ * to redirect loads of the "ManagedStarter" assembly to -+ * Bannerlord.exe. Due to Mono issue #11319, the runtime attempts -+ * to load ManagedStarter before executing the static constructor -+ * that adds this event handler. We work around this by doing the -+ * same thing in our own assembly load hook. */ -+ const char* sgi = getenv("SteamGameId"); -+ if (sgi && !strcmp(sgi, "261550")) -+ { -+ FIXME("hack, using Bannerlord.exe\n"); -+ -+ result = mono_assembly_open("Bannerlord.exe", &stat); -+ -+ if (result) -+ goto done; -+ else -+ { -+ ERR("Bannerlord.exe failed to load\n"); -+ } -+ } -+ } -+ - /* FIXME: We should search the given paths before the GAC. */ - - if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) +@@ -245,7 +245,6 @@ MANUAL_UNIX_THUNKS = { + "vkEnumeratePhysicalDevices", + "vkFreeCommandBuffers", + "vkFreeMemory", +- "vkGetCalibratedTimestampsEXT", + "vkGetDeviceProcAddr", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +@@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceVersion", + "vkFreeCommandBuffers", ++ "vkGetCalibratedTimestampsEXT", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + } From 003223a6816f3121a8e60d8ef3ec79e1768878ce Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Fri, 7 May 2021 13:44:00 -0500 @@ -2967,9 +2893,9 @@ index acba3544e39..9d1b1cde342 100644 +++ b/dlls/dxgi/dxgi_private.h @@ -209,4 +209,6 @@ struct dxgi_surface HRESULT dxgi_surface_init(struct dxgi_surface *surface, IDXGIDevice *device, - IUnknown *outer, struct wined3d_texture *wined3d_texture) DECLSPEC_HIDDEN; + IUnknown *outer, struct wined3d_texture *wined3d_texture); -+HRESULT get_re8_dxgi_factory(REFIID riid, void **factory) DECLSPEC_HIDDEN; ++HRESULT get_re8_dxgi_factory(REFIID riid, void **factory); + #endif /* __WINE_DXGI_PRIVATE_H */ diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c @@ -3206,11 +3132,11 @@ index c84e0a0a638..69c52616e3f 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -32,6 +32,7 @@ - #include "winbase.h" #undef strncpy + #undef wcsncpy -+extern BOOL erms_supported DECLSPEC_HIDDEN; - extern BOOL sse2_supported DECLSPEC_HIDDEN; ++extern BOOL erms_supported; + extern BOOL sse2_supported; #define DBL80_MAX_10_EXP 4932 diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c @@ -3432,293 +3358,6 @@ index cbc53e22ac1..60b03c57137 100644 } -From cdbb70dee8f9db1c983bf7dbc659fc4b77249f9f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?R=C3=A9mi=20Bernon?= -Date: Fri, 4 Jun 2021 10:24:10 +0200 -Subject: [PATCH] wineboot: Compute and write the TSC frequency to registry - ~Mhz. - -In HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor - -Squashed with patches from: - -* Arkadiusz Hiler - -Check if the kernel trusts TSC before using it for Qpc. - -Even if the bits are claiming that TSC meets our requirements the -hardware implementation may still be broken. - -The Linux kernel does a lot of quality testing before deciding to use as -the clock source. If it (or the user, through an override) does not trust -the TSC we should not trust it either. - -* Joshua Ashton - -Some games such as Horizon Zero Dawn use this registry value to -correlate values from rtdsc to real time. - -Testing across a few devices, is seems like Windows always returns the -TSC frequency in this entry, not the current/maximum frequency of the -processor. - -Returning the nominal/maximum cpu frequency here causes the game to run -in slow motion as it may not match the tsc frequency of the processor. - -Ideally we'd not have to measure this and the kernel would return -tsc_khz to userspace, but this is a good enough stop-gap until -https://lkml.org/lkml/2020/12/31/72 or something similar is merged. - -CW-Bug-Id: #18918 -CW-Bug-Id: #18958 ---- - programs/wineboot/wineboot.c | 175 ++++++++++++++++++++++++++++++++++- - 1 file changed, 170 insertions(+), 5 deletions(-) - -diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c -index c05ce580298..731242c75eb 100644 ---- a/programs/wineboot/wineboot.c -+++ b/programs/wineboot/wineboot.c -@@ -82,6 +82,8 @@ - - WINE_DEFAULT_DEBUG_CHANNEL(wineboot); - -+#define TICKSPERSEC 10000000 -+ - extern BOOL shutdown_close_windows( BOOL force ); - extern BOOL shutdown_all_desktops( BOOL force ); - extern void kill_processes( BOOL kill_desktop ); -@@ -241,15 +243,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) - TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); - } - -+static UINT64 read_tsc_frequency( BOOL has_rdtscp ) -+{ -+ UINT64 freq = 0; -+ -+/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, -+ fix it properly and test it on real Intel hardware */ -+ -+#if 0 -+ int regs[4], cpuid_level, tmp; -+ UINT64 denom, numer; -+ -+ __cpuid( regs, 0 ); -+ tmp = regs[2]; -+ regs[2] = regs[3]; -+ regs[3] = tmp; -+ -+ /* only available on some intel CPUs */ -+ if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; -+ else if ((cpuid_level = regs[0]) < 0x15) freq = 0; -+ else -+ { -+ __cpuid( regs, 0x15 ); -+ if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; -+ else -+ { -+ if ((freq = regs[2])) freq = freq * numer / denom; -+ else if (cpuid_level >= 0x16) -+ { -+ __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ -+ freq = regs[0] * (UINT64)1000000; -+ } -+ else freq = 0; -+ } -+ -+ if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); -+ else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); -+ } -+#endif -+ -+ if (freq == 0) -+ { -+ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; -+ unsigned int aux; -+ UINT retries = 50; -+ int regs[4]; -+ -+ do -+ { -+ if (has_rdtscp) -+ { -+ tsc0 = __rdtscp( &aux ); -+ time0 = RtlGetSystemTimePrecise(); -+ tsc1 = __rdtscp( &aux ); -+ Sleep( 1 ); -+ tsc2 = __rdtscp( &aux ); -+ time1 = RtlGetSystemTimePrecise(); -+ tsc3 = __rdtscp( &aux ); -+ } -+ else -+ { -+ tsc0 = __rdtsc(); __cpuid( regs, 0 ); -+ time0 = RtlGetSystemTimePrecise(); -+ tsc1 = __rdtsc(); __cpuid( regs, 0 ); -+ Sleep(1); -+ tsc2 = __rdtsc(); __cpuid( regs, 0 ); -+ time1 = RtlGetSystemTimePrecise(); -+ tsc3 = __rdtsc(); __cpuid( regs, 0 ); -+ } -+ -+ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); -+ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); -+ error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); -+ } -+ while (error > 100 && --retries); -+ -+ if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); -+ else -+ { -+ freq = (freq0 + freq1) / 2; -+ TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); -+ } -+ } -+ -+ return freq; -+} -+ -+static BOOL is_tsc_trusted_by_the_kernel(void) -+{ -+ char buf[4] = {}; -+ DWORD num_read; -+ HANDLE handle; -+ BOOL ret = TRUE; -+ -+ handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", -+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); -+ if (handle == INVALID_HANDLE_VALUE) return TRUE; -+ -+ if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) -+ ret = FALSE; -+ -+ CloseHandle( handle ); -+ return ret; -+} -+ -+static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) -+{ -+ BOOL has_rdtscp = FALSE; -+ int regs[4]; -+ -+ data->QpcBypassEnabled = 0; -+ data->QpcFrequency = TICKSPERSEC; -+ data->QpcShift = 0; -+ data->QpcBias = 0; -+ *tsc_frequency = 0; -+ -+ if (!is_tsc_trusted_by_the_kernel()) -+ { -+ WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); -+ return; -+ } -+ -+ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) -+ { -+ WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); -+ return; -+ } -+ -+ __cpuid( regs, 0x80000000 ); -+ if (regs[0] < 0x80000007) -+ { -+ WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); -+ return; -+ } -+ -+ /* check for invariant tsc bit */ -+ __cpuid( regs, 0x80000007 ); -+ if (!(regs[3] & (1 << 8))) -+ { -+ WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); -+ return; -+ } -+ -+ /* check for rdtscp support bit */ -+ __cpuid( regs, 0x80000001 ); -+ if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; -+ -+ *tsc_frequency = read_tsc_frequency( has_rdtscp ); -+} -+ - #else - - static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) - { - } - -+static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) -+{ -+ data->QpcBypassEnabled = 0; -+ data->QpcFrequency = TICKSPERSEC; -+ data->QpcShift = 0; -+ data->QpcBias = 0; -+ *tsc_frequency = 0; -+} -+ - #endif - --static void create_user_shared_data(void) -+static void create_user_shared_data( UINT64 *tsc_frequency ) - { - struct _KUSER_SHARED_DATA *data; - RTL_OSVERSIONINFOEXW version; -@@ -368,6 +528,7 @@ static void create_user_shared_data(void) - data->ActiveGroupCount = 1; - - initialize_xstate_features( data ); -+ initialize_qpc_features( data, tsc_frequency ); - - UnmapViewOfFile( data ); - } -@@ -660,7 +821,7 @@ static void create_bios_key( HKEY system_key ) - } - - /* create the volatile hardware registry keys */ --static void create_hardware_registry_keys(void) -+static void create_hardware_registry_keys( UINT64 tsc_frequency ) - { - unsigned int i; - HKEY hkey, system_key, cpu_key, fpu_key; -@@ -737,13 +898,16 @@ static void create_hardware_registry_keys(void) - if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, - KEY_ALL_ACCESS, NULL, &hkey, NULL )) - { -+ DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ -+ if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; -+ - RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); - set_reg_value( hkey, L"Identifier", id ); - /* TODO: report ARM properly */ - RegSetValueExA( hkey, "ProcessorNameString", 0, REG_SZ, (const BYTE *)name_buffer, - strlen( (char *)name_buffer ) + 1 ); - set_reg_value( hkey, L"VendorIdentifier", vendorid ); -- RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); -+ RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); - RegCloseKey( hkey ); - } - if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && -@@ -1625,6 +1789,7 @@ int __cdecl main( int argc, char *argv[] ) - BOOL end_session, force, init, kill, restart, shutdown, update; - HANDLE event; - OBJECT_ATTRIBUTES attr; -+ UINT64 tsc_frequency = 0; - UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\KernelObjects\\__wineboot_event" ); - BOOL is_wow64; - -@@ -1710,8 +1875,8 @@ int __cdecl main( int argc, char *argv[] ) - - ResetEvent( event ); /* in case this is a restart */ - -- create_user_shared_data(); -- create_hardware_registry_keys(); -+ create_user_shared_data( &tsc_frequency ); -+ create_hardware_registry_keys( tsc_frequency ); - create_dynamic_registry_keys(); - create_environment_registry_keys(); - create_computer_name_keys(); - - From 3244ba9966c0e7bcd445a65b5c325801f9b77131 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 5 Jan 2022 13:31:14 +0300 @@ -3769,156 +3408,6 @@ index 00000000000..a1884e53243 +# @ stub DllGetClassObject +# @ stub DllRegisterServer +# @ stub DllUnregisterServer -From 9bf094c8a6e962b969a1832613127bc4b7cf0fb3 Mon Sep 17 00:00:00 2001 -From: Paul Gofman -Date: Thu, 10 Feb 2022 16:17:41 +0300 -Subject: [PATCH] ntdll: Guard against syscall stack overrun. - ---- - dlls/ntdll/unix/signal_arm.c | 4 ++++ - dlls/ntdll/unix/signal_arm64.c | 4 ++++ - dlls/ntdll/unix/signal_i386.c | 4 ++++ - dlls/ntdll/unix/signal_x86_64.c | 4 ++++ - dlls/ntdll/unix/unix_private.h | 10 +++++++++- - dlls/ntdll/unix/virtual.c | 5 +++++ - 6 files changed, 30 insertions(+), 1 deletion(-) - -diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c -index eaa41d9e139..ff4dbedccac 100644 ---- a/dlls/ntdll/unix/signal_arm.c -+++ b/dlls/ntdll/unix/signal_arm.c -@@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) - (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), - (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c -index 26910173b81..ba3e3564c1a 100644 ---- a/dlls/ntdll/unix/signal_arm64.c -+++ b/dlls/ntdll/unix/signal_arm64.c -@@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) - (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), - (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c -index 53da5d3ac59..a0afd33ea13 100644 ---- a/dlls/ntdll/unix/signal_i386.c -+++ b/dlls/ntdll/unix/signal_i386.c -@@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, - context->Ebp, context->Esp, context->SegCs, context->SegDs, - context->SegEs, context->SegFs, context->SegGs, context->EFlags ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c -index 7d7f9090986..b1cc682649b 100644 ---- a/dlls/ntdll/unix/signal_x86_64.c -+++ b/dlls/ntdll/unix/signal_x86_64.c -@@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, - TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", - context->R12, context->R13, context->R14, context->R15 ); - -+ if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION -+ && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) -+ ERR_(seh)( "Syscall stack overrun.\n "); -+ - if (ntdll_get_thread_data()->jmp_buf) - { - TRACE_(seh)( "returning to handler\n" ); -diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index 9bc19e75228..4bf0e7cb84e 100644 ---- a/dlls/ntdll/unix/unix_private.h -+++ b/dlls/ntdll/unix/unix_private.h -@@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ - static const SIZE_T signal_stack_mask = 0xffff; - static const SIZE_T signal_stack_size = 0x10000 - 0x3800; - static const SIZE_T kernel_stack_size = 0x100000; --static const SIZE_T min_kernel_stack = 0x2000; -+static const SIZE_T kernel_stack_guard_size = 0x1000; -+static const SIZE_T min_kernel_stack = 0x3000; - static const LONG teb_offset = 0x2000; - - #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) -@@ -331,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) - (char *)ptr < (char *)get_signal_stack() + signal_stack_size); - } - -+static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) -+{ -+ const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; -+ -+ return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); -+} -+ - static inline void mutex_lock( pthread_mutex_t *mutex ) - { - if (!process_exiting) pthread_mutex_lock( mutex ); -diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index e7caf162ee1..10e2bf1477c 100644 ---- a/dlls/ntdll/unix/virtual.c -+++ b/dlls/ntdll/unix/virtual.c -@@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - SIZE_T commit_size, SIZE_T extra_size ) - { - struct file_view *view; -+ char *kernel_stack; - NTSTATUS status; - sigset_t sigset; - SIZE_T size; -@@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - delete_view( view ); - goto done; - } -+ /* setup kernel stack no access guard page */ -+ kernel_stack = (char *)view->base + view->size; -+ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); -+ mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); - } - - /* note: limit is lower than base since the stack grows down */ -From 59b705ed31d38065c12f44e281dc8c18d5024acf Mon Sep 17 00:00:00 2001 -From: Paul Gofman -Date: Fri, 16 Sep 2022 17:14:54 -0500 -Subject: [PATCH] fixup! ntdll: Guard against syscall stack overrun. - -Make guard pages readable. - -CW-Bug-Id: #21305 ---- - dlls/ntdll/unix/virtual.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index 550d7f41dec..8dfe3da0a7e 100644 ---- a/dlls/ntdll/unix/virtual.c -+++ b/dlls/ntdll/unix/virtual.c -@@ -3271,7 +3271,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI - } - /* setup kernel stack no access guard page */ - kernel_stack = (char *)view->base + view->size; -- set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); -+ set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED | VPROT_READ ); - mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); - } - From ba4e9dc41c7fb272c4354669b032b781d96e2e59 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Thu, 19 Dec 2019 09:12:17 -0600 @@ -3967,3 +3456,100 @@ index 0a080ea6be8..667013df5ac 100644 } pXRRFreeScreenResources( screen_resources ); return FALSE; +From 98bce52ddf46c0c8c6be57c27d4aa9c48303798b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Mar 2023 21:12:22 -0600 +Subject: [PATCH] ntdll: HACK: Add WINE_SIMULATE_WRITECOPY option. + +CW-Bug-Id: #22034 + +Edit: Enable for UplayWebCore.exe +--- + dlls/ntdll/unix/loader.c | 5 +++++ + dlls/ntdll/unix/unix_private.h | 1 + + dlls/ntdll/unix/virtual.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 9f00e45..67d071a 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1346,6 +1346,7 @@ const unixlib_entry_t unix_call_wow64_funcs[] = + + BOOL ac_odyssey; + BOOL fsync_simulate_sched_quantum; ++BOOL simulate_writecopy; + + static void hacks_init(void) + { +@@ -1367,6 +1368,10 @@ static void hacks_init(void) + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); + ++ env_str = getenv("WINE_SIMULATE_WRITECOPY"); ++ if (env_str) simulate_writecopy = atoi(env_str); ++ else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; ++ + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) + setenv("WINESTEAMNOEXEC", "1", 0); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2bf8975..8a5baf3 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -175,6 +175,7 @@ extern struct ldt_copy __wine_ldt_copy; + + extern BOOL ac_odyssey; + extern BOOL fsync_simulate_sched_quantum; ++extern BOOL simulate_writecopy; + + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 59c0e6d..9a7af20 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1107,7 +1107,8 @@ static const char *get_prot_str( BYTE prot ) + buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-'; + buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-'); + buffer[2] = (prot & VPROT_READ) ? 'r' : '-'; +- buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-'); ++ buffer[3] = (prot & VPROT_WRITECOPY) ? (prot & VPROT_WRITTEN ? 'w' : 'W') ++ : ((prot & VPROT_WRITE) ? 'w' : '-'); + buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-'; + buffer[5] = 0; + return buffer; +@@ -4730,6 +4731,33 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T + { + old = get_win32_prot( vprot, view->protect ); + status = set_protection( view, base, size, new_prot ); ++ ++ if (simulate_writecopy && status == STATUS_SUCCESS ++ && ((old == PAGE_WRITECOPY || old == PAGE_EXECUTE_WRITECOPY))) ++ { ++ TRACE("Setting VPROT_WRITTEN.\n"); ++ ++ set_page_vprot_bits(base, size, VPROT_WRITTEN, 0); ++ vprot |= VPROT_WRITTEN; ++ old = get_win32_prot( vprot, view->protect ); ++ } ++ else if (status == STATUS_SUCCESS && (view->protect & SEC_IMAGE) && ++ base == (void*)NtCurrentTeb()->Peb->ImageBaseAddress) ++ { ++ /* GTA5 HACK: Mark first page as copied. */ ++ const WCHAR gta5W[] = { 'g','t','a','5','.','e','x','e',0 }; ++ WCHAR *name, *p; ++ ++ name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; ++ p = wcsrchr(name, '\\'); ++ p = p ? p+1 : name; ++ ++ if(!wcsicmp(p, gta5W)) ++ { ++ FIXME("HACK: changing GTA5.exe vprot\n"); ++ set_page_vprot_bits(base, page_size, VPROT_WRITTEN, 0); ++ } ++ } + } + else status = STATUS_NOT_COMMITTED; + } diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-4413e94.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-4413e94.patch new file mode 100644 index 000000000..dbef2809e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-4413e94.patch @@ -0,0 +1,1622 @@ +From 4ad17bc8a5651c65b7e1bced3c9e04bce9f77c91 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 01/12] wined3d: Implement GL texture access callbacks. + +--- + dlls/wined3d/cs.c | 52 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/texture.c | 10 +++++++ + dlls/wined3d/wined3d_private.h | 3 ++ + include/wine/wined3d.h | 5 ++++ + 4 files changed, 70 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 21aaa2e54b6..e291e2b550d 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -77,6 +77,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, ++ WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -457,6 +458,15 @@ struct wined3d_cs_generate_mipmaps + struct wined3d_shader_resource_view *view; + }; + ++struct wined3d_cs_gl_texture_callback ++{ ++ enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; ++ wined3d_gl_texture_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2698,6 +2708,44 @@ void wined3d_device_context_emit_generate_mipmaps(struct wined3d_device_context + wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_gl_texture_callback *op = data; ++ struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ ++ op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ ++ context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); ++ context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); ++ ++ checkGLcall("texture callback\n"); ++ ++ context_release(context); ++ ++} ++ ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_gl_texture_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; ++ op->texture = texture; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2865,6 +2916,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, ++ /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 39e6e2a68b7..bdcd0b55ae6 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4574,6 +4574,16 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); + } + ++void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ ++ TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index f5b1a078907..756d47ef20d 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4762,6 +4762,9 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + cs->c.ops->finish(&cs->c, queue_id); + } + ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index dd1c15f14ed..2123e857a52 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,6 +2969,11 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++ ++void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 34565d56b8cfcdcf787c5a3a0466f990918d1139 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 02/12] wined3d: Implement command stream callbacks. + +--- + dlls/wined3d/cs.c | 40 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/device.c | 8 +++++++ + dlls/wined3d/wined3d_private.h | 2 ++ + include/wine/wined3d.h | 5 +++++ + 4 files changed, 55 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index e291e2b550d..cf55385c3c1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -78,6 +78,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, ++ WINED3D_CS_OP_USER_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -467,6 +468,14 @@ struct wined3d_cs_gl_texture_callback + BYTE data[1]; + }; + ++struct wined3d_cs_user_callback ++{ ++ enum wined3d_cs_op opcode; ++ wined3d_cs_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2749,6 +2758,36 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_user_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_user_callback *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ op->callback(op->data, op->data_size); ++ ++ checkGLcall("user callback\n"); ++ ++ context_release(context); ++} ++ ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_user_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_USER_CALLBACK; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2893,6 +2932,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, ++ /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 484130b3103..286d4d3acf5 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6320,3 +6320,11 @@ LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL + else + return CallWindowProcA(proc, window, message, wparam, lparam); + } ++ ++void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ TRACE("device %p, callback %p, data %p, size %u.\n", device, callback, data, size); ++ ++ wined3d_cs_emit_user_callback(device->cs, callback, data, size); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 756d47ef20d..602647b1e94 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 2123e857a52..39689bdb4e1 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,11 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, con + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size); + ++typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); ++ ++void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 434e12794d7dda50554dbcf781af282816d39121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 03/12] d3d11: Add IWineD3D11Texture2D interface. + +--- + dlls/d3d11/d3d11_private.h | 4 +- + dlls/d3d11/device.c | 4 +- + dlls/d3d11/texture.c | 65 +++++++++++++++++++++----------- + include/Makefile.in | 1 + + include/wine/wined3d-interop.idl | 31 +++++++++++++++ + 5 files changed, 80 insertions(+), 25 deletions(-) + create mode 100644 include/wine/wined3d-interop.idl + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 1a8cdc6d77c..2b1809726ca 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -39,6 +39,8 @@ + #include "wine/winedxgi.h" + #include "wine/rbtree.h" + ++#include "wine/wined3d-interop.h" ++ + struct d3d_device; + + /* TRACE helper functions */ +@@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D + /* ID3D11Texture2D, ID3D10Texture2D */ + struct d3d_texture2d + { +- ID3D11Texture2D ID3D11Texture2D_iface; ++ IWineD3D11Texture2D ID3D11Texture2D_iface; + ID3D10Texture2D ID3D10Texture2D_iface; + LONG refcount; + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 0819918b2a1..41967dd0a1c 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3165,7 +3165,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_device_CreateTexture2D(ID3D11Device2 *ifa + if (FAILED(hr = d3d_texture2d_create(device, desc, data, &object))) + return hr; + +- *texture = &object->ID3D11Texture2D_iface; ++ *texture = (ID3D11Texture2D *)&object->ID3D11Texture2D_iface; + + return S_OK; + } +@@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic + + *wined3d_texture = texture->wined3d_texture; + wined3d_texture_incref(*wined3d_texture); +- ID3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); + + return S_OK; + } +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 061bbb09795..dd21c6d7f08 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -518,14 +518,21 @@ HRESULT d3d_texture1d_create(struct d3d_device *device, const D3D11_TEXTURE1D_DE + + /* ID3D11Texture2D methods */ + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D *iface, REFIID riid, void **object) ++static inline struct d3d_texture2d *impl_from_IWineD3D11Texture2D(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); +- HRESULT hr; ++ return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(IWineD3D11Texture2D *iface, ++ REFIID riid, void **object) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ HRESULT hr; + + TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); + +- if (IsEqualGUID(riid, &IID_ID3D11Texture2D) ++ if (IsEqualGUID(riid, &IID_IWineD3D11Texture2D) ++ || IsEqualGUID(riid, &IID_ID3D11Texture2D) + || IsEqualGUID(riid, &IID_ID3D11Resource) + || IsEqualGUID(riid, &IID_ID3D11DeviceChild) + || IsEqualGUID(riid, &IID_IUnknown)) +@@ -554,9 +561,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedIncrement(&texture->refcount); + + TRACE("%p increasing refcount to %u.\n", texture, refcount); +@@ -572,9 +579,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedDecrement(&texture->refcount); + + TRACE("%p decreasing refcount to %u.\n", texture, refcount); +@@ -594,9 +601,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) + return refcount; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, ID3D11Device **device) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(IWineD3D11Texture2D *iface, ID3D11Device **device) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + + TRACE("iface %p, device %p.\n", iface, device); + +@@ -604,10 +611,10 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, + ID3D11Device_AddRef(*device); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT *data_size, void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -624,10 +631,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D + return d3d_get_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT data_size, const void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -644,10 +651,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D + return d3d_set_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(IWineD3D11Texture2D *iface, + REFGUID guid, const IUnknown *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -664,7 +671,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11T + return d3d_set_private_data_interface(&texture->private_store, guid, data); + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, ++static void STDMETHODCALLTYPE d3d11_texture2d_GetType(IWineD3D11Texture2D *iface, + D3D11_RESOURCE_DIMENSION *resource_dimension) + { + TRACE("iface %p, resource_dimension %p.\n", iface, resource_dimension); +@@ -672,21 +679,21 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, + *resource_dimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(ID3D11Texture2D *iface, UINT eviction_priority) ++static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(IWineD3D11Texture2D *iface, UINT eviction_priority) + { + FIXME("iface %p, eviction_priority %#x stub!\n", iface, eviction_priority); + } + +-static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(ID3D11Texture2D *iface) ++static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(IWineD3D11Texture2D *iface) + { + FIXME("iface %p stub!\n", iface); + + return 0; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_resource_desc wined3d_desc; + + TRACE("iface %p, desc %p.\n", iface, desc); +@@ -708,7 +715,19 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3 + desc->SampleDesc.Quality = wined3d_desc.multisample_quality; + } + +-static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = ++static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, ++ gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ ++ TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ ++ wined3d_mutex_lock(); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ + d3d11_texture2d_QueryInterface, +@@ -725,13 +744,15 @@ static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetEvictionPriority, + /* ID3D11Texture2D methods */ + d3d11_texture2d_GetDesc, ++ /* IWineD3D11Texture methods */ ++ d3d11_texture2d_access_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) + { + if (!iface) + return NULL; +- assert(iface->lpVtbl == &d3d11_texture2d_vtbl); ++ assert(iface->lpVtbl == (void *)&d3d11_texture2d_vtbl); + return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); + } + +diff --git a/include/Makefile.in b/include/Makefile.in +index 9822bce6bdd..d9b646f7de1 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -770,6 +770,7 @@ SOURCES = \ + wine/winbase16.h \ + wine/windef16.h \ + wine/wine_common_ver.rc \ ++ wine/wined3d-interop.idl \ + wine/wined3d.h \ + wine/winedxgi.idl \ + wine/wingdi16.h \ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +new file mode 100644 +index 00000000000..884baf958c2 +--- /dev/null ++++ b/include/wine/wined3d-interop.idl +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2018 JĂ³zef Kucia 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 ++ */ ++ ++#if 0 ++#pragma makedep header install ++#endif ++ ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++ ++import "d3d11.idl"; ++ ++[ ++ object, ++ local, ++ uuid(267dc993-d15e-4015-aaac-b7559e226cc3) ++] ++interface IWineD3D11Texture2D : ID3D11Texture2D ++{ ++ void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From 17c027811a6641eea3298f5cb24c69c796dca328 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 04/12] d3d11: Add IWineD3D11Device interface. + +--- + dlls/d3d11/d3d11_private.h | 1 + + dlls/d3d11/device.c | 52 ++++++++++++++++++++++++++++++++ + include/wine/wined3d-interop.idl | 12 ++++++++ + 3 files changed, 65 insertions(+) + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 2b1809726ca..ec0bd79fdd5 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -564,6 +564,7 @@ struct d3d_device + ID3D10Multithread ID3D10Multithread_iface; + IWineDXGIDeviceParent IWineDXGIDeviceParent_iface; + IUnknown *outer_unk; ++ IWineD3D11Device IWineD3D11Device_iface; + LONG refcount; + + BOOL d3d11_only; +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 41967dd0a1c..887e7de00e9 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3119,6 +3119,53 @@ static ULONG STDMETHODCALLTYPE d3d11_device_Release(ID3D11Device2 *iface) + return IUnknown_Release(device->outer_unk); + } + ++/* IWineD3D11Device methods */ ++ ++static inline struct d3d_device *impl_from_IWineD3D11Device(IWineD3D11Device *iface) ++{ ++ return CONTAINING_RECORD(iface, struct d3d_device, IWineD3D11Device_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE wine_device_QueryInterface(IWineD3D11Device *iface, REFIID riid, void **out) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_QueryInterface(device->outer_unk, riid, out); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_AddRef(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_AddRef(device->outer_unk); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_Release(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_Release(device->outer_unk); ++} ++ ++static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device *iface, ++ user_cs_callback callback, const void *data, unsigned int data_size) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p, callback %p, data %p, data_size %u.\n", iface, callback, data, data_size); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_run_cs_callback(device->wined3d_device, callback, data, data_size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11DeviceVtbl wine_device_vtbl = ++{ ++ /* IUnknown methods */ ++ wine_device_QueryInterface, ++ wine_device_AddRef, ++ wine_device_Release, ++ /* IWineD3D11Device methods */ ++ wine_device_run_on_command_stream, ++}; ++ + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, + const D3D11_SUBRESOURCE_DATA *data, ID3D11Buffer **buffer) + { +@@ -4264,6 +4311,10 @@ static HRESULT STDMETHODCALLTYPE d3d_device_inner_QueryInterface(IUnknown *iface + { + *out = &device->IWineDXGIDeviceParent_iface; + } ++ else if (IsEqualGUID(riid, &IID_IWineD3D11Device)) ++ { ++ *out = &device->IWineD3D11Device_iface; ++ } + else + { + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); +@@ -6648,6 +6699,7 @@ void d3d_device_init(struct d3d_device *device, void *outer_unknown) + device->ID3D10Device1_iface.lpVtbl = &d3d10_device1_vtbl; + device->ID3D10Multithread_iface.lpVtbl = &d3d10_multithread_vtbl; + device->IWineDXGIDeviceParent_iface.lpVtbl = &d3d_dxgi_device_parent_vtbl; ++ device->IWineD3D11Device_iface.lpVtbl = &wine_device_vtbl; + device->device_parent.ops = &d3d_wined3d_device_parent_ops; + device->refcount = 1; + /* COM aggregation always takes place */ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 884baf958c2..d5c91623b3c 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -29,3 +29,15 @@ interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); + } ++ ++typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); ++ ++[ ++ object, ++ local, ++ uuid(8f02de7e-d55d-457b-9423-83456e49c58a) ++] ++interface IWineD3D11Device : IUnknown ++{ ++ void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From af8a3f1e0c505ecb0a2f6a899989431e85013f74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 19 Apr 2018 14:04:49 +0200 +Subject: [PATCH 05/12] wined3d: Implement wined3d_device_wait_idle(). + +--- + dlls/d3d11/device.c | 12 ++++++++++++ + dlls/wined3d/cs.c | 20 ++++++++++++++++++++ + dlls/wined3d/device.c | 7 +++++++ + dlls/wined3d/wined3d_private.h | 1 + + include/wine/wined3d-interop.idl | 2 ++ + include/wine/wined3d.h | 1 + + 6 files changed, 43 insertions(+) + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 887e7de00e9..1ff3f32f2c2 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3156,6 +3156,17 @@ static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device + wined3d_mutex_unlock(); + } + ++static void STDMETHODCALLTYPE wine_device_wait_idle(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_wait_idle(device->wined3d_device); ++ wined3d_mutex_unlock(); ++} ++ + static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + { + /* IUnknown methods */ +@@ -3164,6 +3175,7 @@ static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + wine_device_Release, + /* IWineD3D11Device methods */ + wine_device_run_on_command_stream, ++ wine_device_wait_idle, + }; + + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index cf55385c3c1..0eeabdb6640 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -476,6 +477,11 @@ struct wined3d_cs_user_callback + BYTE data[1]; + }; + ++struct wined3d_cs_wait_idle ++{ ++ enum wined3d_cs_op opcode; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2788,6 +2794,19 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} ++ ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_wait_idle *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_WAIT_IDLE; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 286d4d3acf5..a3e41cb38ef 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6328,3 +6328,10 @@ void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, + + wined3d_cs_emit_user_callback(device->cs, callback, data, size); + } ++ ++void CDECL wined3d_device_wait_idle(struct wined3d_device *device) ++{ ++ TRACE("device %p.\n", device); ++ ++ wined3d_cs_emit_wait_idle(device->cs); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 602647b1e94..8a38f6d4925 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index d5c91623b3c..6f8ea3770e3 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -40,4 +40,6 @@ typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_siz + interface IWineD3D11Device : IUnknown + { + void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++ ++ void wait_idle(); + } +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 39689bdb4e1..c90a71dbd5b 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2978,6 +2978,7 @@ typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size) + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, + wined3d_cs_callback callback, const void *data, unsigned int size); ++void __cdecl wined3d_device_wait_idle(struct wined3d_device *device); + + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) +-- +2.30.2 + +From 9c84c7ec7714dc3f0f9038b60735788f9d37d77c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 1 May 2018 15:06:30 -0500 +Subject: [PATCH 06/12] wined3d: Support retrieving depth texture in GL texture + callback + +--- + dlls/d3d11/texture.c | 20 +++++++++++++++++--- + dlls/wined3d/cs.c | 14 ++++++++++++-- + dlls/wined3d/texture.c | 7 ++++--- + dlls/wined3d/wined3d_private.h | 3 ++- + include/wine/wined3d-interop.idl | 4 ++-- + include/wine/wined3d.h | 4 ++-- + 6 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index dd21c6d7f08..c46aae99a24 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,14 +716,28 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, const void *data, unsigned int size) ++ gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ IWineD3D11Texture2D *depth_d3d11 = NULL; + + TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); + + wined3d_mutex_lock(); +- wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ ++ if (depth_unk) ++ { ++ HRESULT hr; ++ hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); ++ if(hr == S_OK) ++ depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); ++ } ++ ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ ++ if (depth_d3d11) ++ IWineD3D11Texture2D_Release(depth_d3d11); ++ + wined3d_mutex_unlock(); + } + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 0eeabdb6640..edfdeecee3c 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -464,6 +464,7 @@ struct wined3d_cs_gl_texture_callback + { + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; ++ struct wined3d_texture *depth_texture; + wined3d_gl_texture_callback callback; + unsigned int data_size; + BYTE data[1]; +@@ -2727,6 +2728,7 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + { + const struct wined3d_cs_gl_texture_callback *op = data; + struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ struct wined3d_texture_gl *depth_texture = wined3d_texture_gl(op->depth_texture); + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + +@@ -2734,8 +2736,12 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + gl_info = wined3d_context_gl(context)->gl_info; + + wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ if (depth_texture) ++ wined3d_texture_load_location(&depth_texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + +- op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ op->callback(texture->texture_rgb.name, ++ depth_texture ? depth_texture->texture_rgb.name : 0, ++ op->data, op->data_size); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +@@ -2750,13 +2750,15 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_cs_gl_texture_callback *op; + + op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; + op->texture = texture; ++ op->depth_texture = depth_texture; + op->callback = callback; + op->data_size = size; + memcpy(op->data, data, size); +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index bdcd0b55ae6..b8fa5d7d3ae 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4575,13 +4575,14 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + } + + void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_device *device = texture->resource.device; + +- TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ TRACE("texture %p, depth_texture %p, callback %p, data %p, size %u.\n", texture, depth_texture, callback, data, size); + +- wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 8a38f6d4925..a01ee1690c1 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4763,7 +4763,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 6f8ea3770e3..c5395ccc0ed 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -16,7 +16,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +-typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size); + + import "d3d11.idl"; + +@@ -27,7 +27,7 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index c90a71dbd5b..81235a7677c 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,10 +2969,10 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + +-typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int size); + + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + +-- +2.30.2 + +From d00f400da6cc085c207d0501944804ad63cb486b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 23:10:14 +0200 +Subject: [PATCH 07/12] wined3d: Get rid of wined3d_cs_emit_wait_idle(). + +--- + dlls/wined3d/cs.c | 15 --------------- + dlls/wined3d/device.c | 2 +- + dlls/wined3d/wined3d_private.h | 1 - + 3 files changed, 1 insertion(+), 17 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index edfdeecee3c..298f74c46c0 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -2804,19 +2803,6 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + +-static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} +- +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) +-{ +- struct wined3d_cs_wait_idle *op; +- +- op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); +- op->opcode = WINED3D_CS_OP_WAIT_IDLE; +- +- cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +- cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); +-} +- + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index a3e41cb38ef..9ec837e837d 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6333,5 +6333,5 @@ void CDECL wined3d_device_wait_idle(struct wined3d_device *device) + { + TRACE("device %p.\n", device); + +- wined3d_cs_emit_wait_idle(device->cs); ++ device->cs->c.ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index a01ee1690c1..0a18d909c5e 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +-- +2.30.2 + +From 08d1dd39399a79f3f701cbcb37b733a5f48af5fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 22:04:21 +0200 +Subject: [PATCH 08/12] wined3d: Implement synchronous texture access. + +For vrclient. +--- + dlls/d3d11/texture.c | 15 ++++ + dlls/wined3d/cs.c | 48 ++++++++++++ + dlls/wined3d/device.c | 2 + + dlls/wined3d/texture.c | 123 +++++++++++++++++++++++++++++++ + dlls/wined3d/wined3d_private.h | 15 ++++ + include/wine/wined3d-interop.idl | 2 + + include/wine/wined3d.h | 2 + + 7 files changed, 207 insertions(+) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index c46aae99a24..7f7e8254225 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -741,6 +741,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Textur + wined3d_mutex_unlock(); + } + ++static unsigned int STDMETHODCALLTYPE d3d11_texture2d_get_gl_texture(IWineD3D11Texture2D *iface) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ unsigned int id; ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ id = wined3d_get_gl_texture(texture->wined3d_texture); ++ wined3d_mutex_unlock(); ++ ++ return id; ++} ++ + static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ +@@ -760,6 +774,7 @@ static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetDesc, + /* IWineD3D11Texture methods */ + d3d11_texture2d_access_gl_texture, ++ d3d11_texture2d_get_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 298f74c46c0..511883924a1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -79,6 +79,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_USER_CALLBACK, ++ WINED3D_CS_OP_FENCE, + WINED3D_CS_OP_STOP, + }; + +@@ -482,6 +483,12 @@ struct wined3d_cs_wait_idle + enum wined3d_cs_op opcode; + }; + ++struct wined3d_cs_fence ++{ ++ enum wined3d_cs_op opcode; ++ GLsync *fence; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2803,6 +2810,46 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_fence *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ GLsync fence; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); ++ wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); ++ ++ *op->fence = fence; ++ ++ checkGLcall("fence"); ++ ++ context_release(context); ++} ++ ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_fence *op; ++ GLsync fence; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_FENCE; ++ op->fence = &fence; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++ ++ return fence; ++} ++ ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++{ ++ return wined3d_cs_emit_fence(cs); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2918,6 +2965,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, ++ /* WINED3D_CS_OP_FENCE */ wined3d_cs_exec_fence, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 9ec837e837d..9d042f4d0e1 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -218,6 +218,8 @@ void wined3d_device_cleanup(struct wined3d_device *device) + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + ++ wined3d_destroy_gl_vr_context(&device->vr_context); ++ + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index b8fa5d7d3ae..25c5e57fca0 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4585,6 +4585,129 @@ void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + ++static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) ++{ ++ const struct wined3d_adapter *adapter = device->adapter; ++ const struct wined3d_gl_info *gl_info = &adapter->gl_info; ++ struct wined3d_vr_gl_context *ctx = &device->vr_context; ++ PIXELFORMATDESCRIPTOR pfd; ++ int pixel_format; ++ HGLRC share_ctx; ++ ++ if (ctx->gl_info) ++ return gl_info; ++ ++ TRACE("Creating GL context.\n"); ++ ++ if (!gl_info->p_wglCreateContextAttribsARB) ++ { ++ ERR("wglCreateContextAttribsARB is not supported.\n"); ++ return NULL; ++ } ++ ++ if (!gl_info->supported[ARB_SYNC]) ++ { ++ FIXME("ARB_sync is not supported.\n"); ++ return NULL; ++ } ++ ++ ctx->window = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D VR window", ++ WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); ++ if (!ctx->window) ++ { ++ ERR("Failed to create a window.\n"); ++ return NULL; ++ } ++ ++ ctx->dc = GetDC(ctx->window); ++ if (!ctx->dc) ++ { ++ ERR("Failed to get a DC.\n"); ++ goto fail; ++ } ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd.nSize = sizeof(pfd); ++ pfd.nVersion = 1; ++ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; ++ pfd.iPixelType = PFD_TYPE_RGBA; ++ pfd.cColorBits = 32; ++ pfd.iLayerType = PFD_MAIN_PLANE; ++ ++ if (!(pixel_format = ChoosePixelFormat(ctx->dc, &pfd))) ++ { ++ ERR("Failed to find a suitable pixel format.\n"); ++ goto fail; ++ } ++ DescribePixelFormat(ctx->dc, pixel_format, sizeof(pfd), &pfd); ++ SetPixelFormat(ctx->dc, pixel_format, &pfd); ++ ++ share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; ++ if (!(ctx->gl_ctx = context_create_wgl_attribs(gl_info, ctx->dc, share_ctx))) ++ { ++ WARN("Failed to create GL context for VR.\n"); ++ goto fail; ++ } ++ ++ if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) ++ { ++ ERR("Failed to make GL context current.\n"); ++ goto fail; ++ } ++ ++ checkGLcall("create context"); ++ ++ ctx->gl_info = gl_info; ++ return gl_info; ++ ++fail: ++ if (ctx->gl_ctx) ++ wglDeleteContext(ctx->gl_ctx); ++ ctx->gl_ctx = NULL; ++ if (ctx->dc) ++ ReleaseDC(ctx->window, ctx->dc); ++ ctx->dc = NULL; ++ if (ctx->window) ++ DestroyWindow(ctx->window); ++ ctx->window = NULL; ++ return NULL; ++} ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) ++{ ++ if (!ctx->gl_info) ++ return; ++ ++ TRACE("Destroying GL context.\n"); ++ ++ wglMakeCurrent(NULL, NULL); ++ wglDeleteContext(ctx->gl_ctx); ++ ReleaseDC(ctx->window, ctx->dc); ++ DestroyWindow(ctx->window); ++} ++ ++unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_texture_gl *gl_texture; ++ GLsync fence; ++ ++ TRACE("texture %p.\n", texture); ++ ++ if (!(gl_info = wined3d_prepare_vr_gl_context(device))) ++ return 0; ++ ++ fence = wined3d_cs_synchronize(device->cs); ++ GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); ++ GL_EXTCALL(glDeleteSync(fence)); ++ ++ checkGLcall("synchronize CS"); ++ ++ gl_texture = wined3d_texture_gl(texture); ++ return gl_texture->texture_rgb.name; ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 0a18d909c5e..4bd32d95cda 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry + struct wined3d_stream_output_element elements[1]; + }; + ++struct wined3d_vr_gl_context ++{ ++ HWND window; ++ HDC dc; ++ HGLRC gl_ctx; ++ const struct wined3d_gl_info *gl_info; ++}; ++ + struct wined3d_device + { + LONG ref; +@@ -3842,6 +3850,8 @@ struct wined3d_device + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; ++ ++ struct wined3d_vr_gl_context vr_context; + + CRITICAL_SECTION bo_map_lock; + }; +@@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++ ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index c5395ccc0ed..b1bda8ada76 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -28,6 +28,8 @@ import "d3d11.idl"; + interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ ++ unsigned int get_gl_texture(); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 81235a7677c..d63c54ca593 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,8 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, uns + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + ++unsigned int __cdecl wined3d_get_gl_texture(struct wined3d_texture *texture); ++ + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, +-- +2.30.2 + +From cc1f77d00ee61e8620029c08110015543682acb8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:04:07 +0200 +Subject: [PATCH 09/12] wined3d: Load TEXTURE_RGB location for synchronous + texture access. + +--- + dlls/wined3d/cs.c | 10 +++++++--- + dlls/wined3d/texture.c | 2 +- + dlls/wined3d/wined3d_private.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 511883924a1..6242651dd72 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -486,6 +486,7 @@ struct wined3d_cs_wait_idle + struct wined3d_cs_fence + { + enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; + GLsync *fence; + }; + +@@ -2820,6 +2821,8 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context = context_acquire(cs->c.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + ++ wined3d_texture_load_location(op->texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ + fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); + +@@ -2830,13 +2833,14 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context_release(context); + } + +-static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs, struct wined3d_texture *texture) + { + struct wined3d_cs_fence *op; + GLsync fence; + + op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FENCE; ++ op->texture = texture; + op->fence = &fence; + + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +@@ -2845,9 +2849,9 @@ static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) + return fence; + } + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) + { +- return wined3d_cs_emit_fence(cs); ++ return wined3d_cs_emit_fence(cs, texture); + } + + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 25c5e57fca0..72a64889ca9 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4698,7 +4698,7 @@ unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) + if (!(gl_info = wined3d_prepare_vr_gl_context(device))) + return 0; + +- fence = wined3d_cs_synchronize(device->cs); ++ fence = wined3d_cs_synchronize(device->cs, texture); + GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); + GL_EXTCALL(glDeleteSync(fence)); + +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 4bd32d95cda..2a9b2c08877 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; + + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + +-- +2.30.2 + +From 29f4110ef25083ad84d3f441986345abf79f3eaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:17:15 +0200 +Subject: [PATCH 10/12] d3d11: Pass IWineD3D11Texture2D to access_gl_texture(). + +--- + dlls/d3d11/texture.c | 21 +++++++-------------- + include/wine/wined3d-interop.idl | 3 ++- + 2 files changed, 9 insertions(+), 15 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 7f7e8254225..32bf3c3c9cc 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,27 +716,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) ++ gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { + struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; +- IWineD3D11Texture2D *depth_d3d11 = NULL; ++ struct wined3d_texture *wined3d_depth_texture = NULL; + +- TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", ++ iface, callback, depth_texture, data, size); + + wined3d_mutex_lock(); + +- if (depth_unk) +- { +- HRESULT hr; +- hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); +- if(hr == S_OK) +- depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); +- } +- +- wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ if (depth_texture) ++ wined3d_depth_texture = impl_from_IWineD3D11Texture2D(depth_texture)->wined3d_texture; + +- if (depth_d3d11) +- IWineD3D11Texture2D_Release(depth_d3d11); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, wined3d_depth_texture, data, size); + + wined3d_mutex_unlock(); + } +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index b1bda8ada76..f960e2f6d9d 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -27,7 +27,8 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, ++ IWineD3D11Texture2D *depth_texture, const void *data, unsigned int data_size); + + unsigned int get_gl_texture(); + } +-- +2.30.2 + +From 9a97a24ad5001cc4d25b9b8236fbf13b76d9eaef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 11 Oct 2018 11:31:43 +0200 +Subject: [PATCH 11/12] d3d11: Remove unused 'depth_tex' variable. + +--- + dlls/d3d11/texture.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 32bf3c3c9cc..f6e7d474d99 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -718,7 +718,7 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, + gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_texture *wined3d_depth_texture = NULL; + + TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", +-- +2.30.2 + +From b476c31f53be82ad3b8a3ec9d7987f75e8040cfe Mon Sep 17 00:00:00 2001 +From: GloriousEggroll +Date: Sun, 18 Aug 2019 19:05:52 -0600 +Subject: [PATCH 12/12] wined3d + +--- + dlls/wined3d/wined3d.spec | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec +index 2879e1653d3..bbc9ce7b99c 100644 +--- a/dlls/wined3d/wined3d.spec ++++ b/dlls/wined3d/wined3d.spec +@@ -367,6 +367,11 @@ + @ cdecl wined3d_vertex_declaration_decref(ptr) + @ cdecl wined3d_vertex_declaration_get_parent(ptr) + @ cdecl wined3d_vertex_declaration_incref(ptr) ++ ++@ cdecl wined3d_access_gl_texture(ptr ptr ptr long) ++@ cdecl wined3d_device_run_cs_callback(ptr ptr ptr long) ++@ cdecl wined3d_device_wait_idle(ptr) ++@ cdecl wined3d_get_gl_texture(ptr) + + @ cdecl vkd3d_create_instance(ptr ptr) + @ cdecl vkd3d_instance_decref(ptr) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-5bc6ab5.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-5bc6ab5.patch new file mode 100644 index 000000000..757781ab8 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-5bc6ab5.patch @@ -0,0 +1,1622 @@ +From 4ad17bc8a5651c65b7e1bced3c9e04bce9f77c91 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 01/12] wined3d: Implement GL texture access callbacks. + +--- + dlls/wined3d/cs.c | 52 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/texture.c | 10 +++++++ + dlls/wined3d/wined3d_private.h | 3 ++ + include/wine/wined3d.h | 5 ++++ + 4 files changed, 70 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 21aaa2e54b6..e291e2b550d 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -77,6 +77,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, ++ WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -457,6 +458,15 @@ struct wined3d_cs_generate_mipmaps + struct wined3d_shader_resource_view *view; + }; + ++struct wined3d_cs_gl_texture_callback ++{ ++ enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; ++ wined3d_gl_texture_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2698,6 +2708,44 @@ void wined3d_device_context_emit_generate_mipmaps(struct wined3d_device_context + wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_gl_texture_callback *op = data; ++ struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ ++ op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ ++ context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); ++ context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); ++ ++ checkGLcall("texture callback\n"); ++ ++ context_release(context); ++ ++} ++ ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_gl_texture_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; ++ op->texture = texture; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2865,6 +2916,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, ++ /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 39e6e2a68b7..bdcd0b55ae6 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4574,6 +4574,16 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); + } + ++void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ ++ TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index f5b1a078907..756d47ef20d 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4762,6 +4762,9 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + cs->c.ops->finish(&cs->c, queue_id); + } + ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index dd1c15f14ed..2123e857a52 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,6 +2969,11 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++ ++void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 34565d56b8cfcdcf787c5a3a0466f990918d1139 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 02/12] wined3d: Implement command stream callbacks. + +--- + dlls/wined3d/cs.c | 40 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/device.c | 8 +++++++ + dlls/wined3d/wined3d_private.h | 2 ++ + include/wine/wined3d.h | 5 +++++ + 4 files changed, 55 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index e291e2b550d..cf55385c3c1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -78,6 +78,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, ++ WINED3D_CS_OP_USER_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -467,6 +468,14 @@ struct wined3d_cs_gl_texture_callback + BYTE data[1]; + }; + ++struct wined3d_cs_user_callback ++{ ++ enum wined3d_cs_op opcode; ++ wined3d_cs_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2749,6 +2758,36 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_user_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_user_callback *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ op->callback(op->data, op->data_size); ++ ++ checkGLcall("user callback\n"); ++ ++ context_release(context); ++} ++ ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_user_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_USER_CALLBACK; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2893,6 +2932,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, ++ /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 484130b3103..286d4d3acf5 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6320,3 +6320,11 @@ LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL + else + return CallWindowProcA(proc, window, message, wparam, lparam); + } ++ ++void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ TRACE("device %p, callback %p, data %p, size %u.\n", device, callback, data, size); ++ ++ wined3d_cs_emit_user_callback(device->cs, callback, data, size); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 756d47ef20d..602647b1e94 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 2123e857a52..39689bdb4e1 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,11 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, con + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size); + ++typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); ++ ++void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 434e12794d7dda50554dbcf781af282816d39121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 03/12] d3d11: Add IWineD3D11Texture2D interface. + +--- + dlls/d3d11/d3d11_private.h | 4 +- + dlls/d3d11/device.c | 4 +- + dlls/d3d11/texture.c | 65 +++++++++++++++++++++----------- + include/Makefile.in | 1 + + include/wine/wined3d-interop.idl | 31 +++++++++++++++ + 5 files changed, 80 insertions(+), 25 deletions(-) + create mode 100644 include/wine/wined3d-interop.idl + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 1a8cdc6d77c..2b1809726ca 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -39,6 +39,8 @@ + #include "wine/winedxgi.h" + #include "wine/rbtree.h" + ++#include "wine/wined3d-interop.h" ++ + struct d3d_device; + + /* TRACE helper functions */ +@@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D + /* ID3D11Texture2D, ID3D10Texture2D */ + struct d3d_texture2d + { +- ID3D11Texture2D ID3D11Texture2D_iface; ++ IWineD3D11Texture2D ID3D11Texture2D_iface; + ID3D10Texture2D ID3D10Texture2D_iface; + LONG refcount; + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 0819918b2a1..41967dd0a1c 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3165,7 +3165,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_device_CreateTexture2D(ID3D11Device2 *ifa + if (FAILED(hr = d3d_texture2d_create(device, desc, data, &object))) + return hr; + +- *texture = &object->ID3D11Texture2D_iface; ++ *texture = (ID3D11Texture2D *)&object->ID3D11Texture2D_iface; + + return S_OK; + } +@@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic + return hr; + + hr = IUnknown_QueryInterface(object->dxgi_resource, &IID_IDXGISurface, (void **)ret_surface); +- ID3D11Texture2D_Release(&object->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&object->ID3D11Texture2D_iface); + return hr; + } + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 061bbb09795..dd21c6d7f08 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -518,14 +518,21 @@ HRESULT d3d_texture1d_create(struct d3d_device *device, const D3D11_TEXTURE1D_DE + + /* ID3D11Texture2D methods */ + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D *iface, REFIID riid, void **object) ++static inline struct d3d_texture2d *impl_from_IWineD3D11Texture2D(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); +- HRESULT hr; ++ return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(IWineD3D11Texture2D *iface, ++ REFIID riid, void **object) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ HRESULT hr; + + TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); + +- if (IsEqualGUID(riid, &IID_ID3D11Texture2D) ++ if (IsEqualGUID(riid, &IID_IWineD3D11Texture2D) ++ || IsEqualGUID(riid, &IID_ID3D11Texture2D) + || IsEqualGUID(riid, &IID_ID3D11Resource) + || IsEqualGUID(riid, &IID_ID3D11DeviceChild) + || IsEqualGUID(riid, &IID_IUnknown)) +@@ -554,9 +561,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedIncrement(&texture->refcount); + + TRACE("%p increasing refcount to %u.\n", texture, refcount); +@@ -572,9 +579,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedDecrement(&texture->refcount); + + TRACE("%p decreasing refcount to %u.\n", texture, refcount); +@@ -594,9 +601,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) + return refcount; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, ID3D11Device **device) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(IWineD3D11Texture2D *iface, ID3D11Device **device) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + + TRACE("iface %p, device %p.\n", iface, device); + +@@ -604,10 +611,10 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, + ID3D11Device_AddRef(*device); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT *data_size, void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -624,10 +631,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D + return d3d_get_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT data_size, const void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -644,10 +651,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D + return d3d_set_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(IWineD3D11Texture2D *iface, + REFGUID guid, const IUnknown *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -664,7 +671,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11T + return d3d_set_private_data_interface(&texture->private_store, guid, data); + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, ++static void STDMETHODCALLTYPE d3d11_texture2d_GetType(IWineD3D11Texture2D *iface, + D3D11_RESOURCE_DIMENSION *resource_dimension) + { + TRACE("iface %p, resource_dimension %p.\n", iface, resource_dimension); +@@ -672,21 +679,21 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, + *resource_dimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(ID3D11Texture2D *iface, UINT eviction_priority) ++static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(IWineD3D11Texture2D *iface, UINT eviction_priority) + { + FIXME("iface %p, eviction_priority %#x stub!\n", iface, eviction_priority); + } + +-static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(ID3D11Texture2D *iface) ++static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(IWineD3D11Texture2D *iface) + { + FIXME("iface %p stub!\n", iface); + + return 0; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_resource_desc wined3d_desc; + + TRACE("iface %p, desc %p.\n", iface, desc); +@@ -708,7 +715,19 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3 + desc->SampleDesc.Quality = wined3d_desc.multisample_quality; + } + +-static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = ++static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, ++ gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ ++ TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ ++ wined3d_mutex_lock(); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ + d3d11_texture2d_QueryInterface, +@@ -725,13 +744,15 @@ static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetEvictionPriority, + /* ID3D11Texture2D methods */ + d3d11_texture2d_GetDesc, ++ /* IWineD3D11Texture methods */ ++ d3d11_texture2d_access_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) + { + if (!iface) + return NULL; +- assert(iface->lpVtbl == &d3d11_texture2d_vtbl); ++ assert(iface->lpVtbl == (void *)&d3d11_texture2d_vtbl); + return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); + } + +diff --git a/include/Makefile.in b/include/Makefile.in +index 9822bce6bdd..d9b646f7de1 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -770,6 +770,7 @@ SOURCES = \ + wine/winbase16.h \ + wine/windef16.h \ + wine/wine_common_ver.rc \ ++ wine/wined3d-interop.idl \ + wine/wined3d.h \ + wine/winedxgi.idl \ + wine/wingdi16.h \ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +new file mode 100644 +index 00000000000..884baf958c2 +--- /dev/null ++++ b/include/wine/wined3d-interop.idl +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2018 JĂ³zef Kucia 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 ++ */ ++ ++#if 0 ++#pragma makedep header install ++#endif ++ ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++ ++import "d3d11.idl"; ++ ++[ ++ object, ++ local, ++ uuid(267dc993-d15e-4015-aaac-b7559e226cc3) ++] ++interface IWineD3D11Texture2D : ID3D11Texture2D ++{ ++ void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From 17c027811a6641eea3298f5cb24c69c796dca328 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 04/12] d3d11: Add IWineD3D11Device interface. + +--- + dlls/d3d11/d3d11_private.h | 1 + + dlls/d3d11/device.c | 52 ++++++++++++++++++++++++++++++++ + include/wine/wined3d-interop.idl | 12 ++++++++ + 3 files changed, 65 insertions(+) + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 2b1809726ca..ec0bd79fdd5 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -564,6 +564,7 @@ struct d3d_device + ID3D10Multithread ID3D10Multithread_iface; + IWineDXGIDeviceParent IWineDXGIDeviceParent_iface; + IUnknown *outer_unk; ++ IWineD3D11Device IWineD3D11Device_iface; + LONG refcount; + + BOOL d3d11_only; +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 41967dd0a1c..887e7de00e9 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3119,6 +3119,53 @@ static ULONG STDMETHODCALLTYPE d3d11_device_Release(ID3D11Device2 *iface) + return IUnknown_Release(device->outer_unk); + } + ++/* IWineD3D11Device methods */ ++ ++static inline struct d3d_device *impl_from_IWineD3D11Device(IWineD3D11Device *iface) ++{ ++ return CONTAINING_RECORD(iface, struct d3d_device, IWineD3D11Device_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE wine_device_QueryInterface(IWineD3D11Device *iface, REFIID riid, void **out) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_QueryInterface(device->outer_unk, riid, out); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_AddRef(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_AddRef(device->outer_unk); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_Release(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_Release(device->outer_unk); ++} ++ ++static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device *iface, ++ user_cs_callback callback, const void *data, unsigned int data_size) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p, callback %p, data %p, data_size %u.\n", iface, callback, data, data_size); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_run_cs_callback(device->wined3d_device, callback, data, data_size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11DeviceVtbl wine_device_vtbl = ++{ ++ /* IUnknown methods */ ++ wine_device_QueryInterface, ++ wine_device_AddRef, ++ wine_device_Release, ++ /* IWineD3D11Device methods */ ++ wine_device_run_on_command_stream, ++}; ++ + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, + const D3D11_SUBRESOURCE_DATA *data, ID3D11Buffer **buffer) + { +@@ -4264,6 +4311,10 @@ static HRESULT STDMETHODCALLTYPE d3d_device_inner_QueryInterface(IUnknown *iface + { + *out = &device->IWineDXGIDeviceParent_iface; + } ++ else if (IsEqualGUID(riid, &IID_IWineD3D11Device)) ++ { ++ *out = &device->IWineD3D11Device_iface; ++ } + else + { + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); +@@ -6648,6 +6699,7 @@ void d3d_device_init(struct d3d_device *device, void *outer_unknown) + device->ID3D10Device1_iface.lpVtbl = &d3d10_device1_vtbl; + device->ID3D10Multithread_iface.lpVtbl = &d3d10_multithread_vtbl; + device->IWineDXGIDeviceParent_iface.lpVtbl = &d3d_dxgi_device_parent_vtbl; ++ device->IWineD3D11Device_iface.lpVtbl = &wine_device_vtbl; + device->device_parent.ops = &d3d_wined3d_device_parent_ops; + device->refcount = 1; + /* COM aggregation always takes place */ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 884baf958c2..d5c91623b3c 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -29,3 +29,15 @@ interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); + } ++ ++typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); ++ ++[ ++ object, ++ local, ++ uuid(8f02de7e-d55d-457b-9423-83456e49c58a) ++] ++interface IWineD3D11Device : IUnknown ++{ ++ void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From af8a3f1e0c505ecb0a2f6a899989431e85013f74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 19 Apr 2018 14:04:49 +0200 +Subject: [PATCH 05/12] wined3d: Implement wined3d_device_wait_idle(). + +--- + dlls/d3d11/device.c | 12 ++++++++++++ + dlls/wined3d/cs.c | 20 ++++++++++++++++++++ + dlls/wined3d/device.c | 7 +++++++ + dlls/wined3d/wined3d_private.h | 1 + + include/wine/wined3d-interop.idl | 2 ++ + include/wine/wined3d.h | 1 + + 6 files changed, 43 insertions(+) + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 887e7de00e9..1ff3f32f2c2 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3156,6 +3156,17 @@ static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device + wined3d_mutex_unlock(); + } + ++static void STDMETHODCALLTYPE wine_device_wait_idle(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_wait_idle(device->wined3d_device); ++ wined3d_mutex_unlock(); ++} ++ + static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + { + /* IUnknown methods */ +@@ -3164,6 +3175,7 @@ static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + wine_device_Release, + /* IWineD3D11Device methods */ + wine_device_run_on_command_stream, ++ wine_device_wait_idle, + }; + + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index cf55385c3c1..0eeabdb6640 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -476,6 +477,11 @@ struct wined3d_cs_user_callback + BYTE data[1]; + }; + ++struct wined3d_cs_wait_idle ++{ ++ enum wined3d_cs_op opcode; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2788,6 +2794,19 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} ++ ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_wait_idle *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_WAIT_IDLE; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 286d4d3acf5..a3e41cb38ef 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6328,3 +6328,10 @@ void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, + + wined3d_cs_emit_user_callback(device->cs, callback, data, size); + } ++ ++void CDECL wined3d_device_wait_idle(struct wined3d_device *device) ++{ ++ TRACE("device %p.\n", device); ++ ++ wined3d_cs_emit_wait_idle(device->cs); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 602647b1e94..8a38f6d4925 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index d5c91623b3c..6f8ea3770e3 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -40,4 +40,6 @@ typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_siz + interface IWineD3D11Device : IUnknown + { + void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++ ++ void wait_idle(); + } +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 39689bdb4e1..c90a71dbd5b 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2978,6 +2978,7 @@ typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size) + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, + wined3d_cs_callback callback, const void *data, unsigned int size); ++void __cdecl wined3d_device_wait_idle(struct wined3d_device *device); + + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) +-- +2.30.2 + +From 9c84c7ec7714dc3f0f9038b60735788f9d37d77c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 1 May 2018 15:06:30 -0500 +Subject: [PATCH 06/12] wined3d: Support retrieving depth texture in GL texture + callback + +--- + dlls/d3d11/texture.c | 20 +++++++++++++++++--- + dlls/wined3d/cs.c | 14 ++++++++++++-- + dlls/wined3d/texture.c | 7 ++++--- + dlls/wined3d/wined3d_private.h | 3 ++- + include/wine/wined3d-interop.idl | 4 ++-- + include/wine/wined3d.h | 4 ++-- + 6 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index dd21c6d7f08..c46aae99a24 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,14 +716,28 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, const void *data, unsigned int size) ++ gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ IWineD3D11Texture2D *depth_d3d11 = NULL; + + TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); + + wined3d_mutex_lock(); +- wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ ++ if (depth_unk) ++ { ++ HRESULT hr; ++ hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); ++ if(hr == S_OK) ++ depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); ++ } ++ ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ ++ if (depth_d3d11) ++ IWineD3D11Texture2D_Release(depth_d3d11); ++ + wined3d_mutex_unlock(); + } + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 0eeabdb6640..edfdeecee3c 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -464,6 +464,7 @@ struct wined3d_cs_gl_texture_callback + { + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; ++ struct wined3d_texture *depth_texture; + wined3d_gl_texture_callback callback; + unsigned int data_size; + BYTE data[1]; +@@ -2727,6 +2728,7 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + { + const struct wined3d_cs_gl_texture_callback *op = data; + struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ struct wined3d_texture_gl *depth_texture = wined3d_texture_gl(op->depth_texture); + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + +@@ -2734,8 +2736,12 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + gl_info = wined3d_context_gl(context)->gl_info; + + wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ if (depth_texture) ++ wined3d_texture_load_location(&depth_texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + +- op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ op->callback(texture->texture_rgb.name, ++ depth_texture ? depth_texture->texture_rgb.name : 0, ++ op->data, op->data_size); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +@@ -2750,13 +2750,15 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_cs_gl_texture_callback *op; + + op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; + op->texture = texture; ++ op->depth_texture = depth_texture; + op->callback = callback; + op->data_size = size; + memcpy(op->data, data, size); +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index bdcd0b55ae6..b8fa5d7d3ae 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4575,13 +4575,14 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + } + + void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_device *device = texture->resource.device; + +- TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ TRACE("texture %p, depth_texture %p, callback %p, data %p, size %u.\n", texture, depth_texture, callback, data, size); + +- wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 8a38f6d4925..a01ee1690c1 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4763,7 +4763,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 6f8ea3770e3..c5395ccc0ed 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -16,7 +16,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +-typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size); + + import "d3d11.idl"; + +@@ -27,7 +27,7 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index c90a71dbd5b..81235a7677c 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,10 +2969,10 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + +-typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int size); + + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + +-- +2.30.2 + +From d00f400da6cc085c207d0501944804ad63cb486b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 23:10:14 +0200 +Subject: [PATCH 07/12] wined3d: Get rid of wined3d_cs_emit_wait_idle(). + +--- + dlls/wined3d/cs.c | 15 --------------- + dlls/wined3d/device.c | 2 +- + dlls/wined3d/wined3d_private.h | 1 - + 3 files changed, 1 insertion(+), 17 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index edfdeecee3c..298f74c46c0 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -2804,19 +2803,6 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + +-static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} +- +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) +-{ +- struct wined3d_cs_wait_idle *op; +- +- op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); +- op->opcode = WINED3D_CS_OP_WAIT_IDLE; +- +- cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +- cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); +-} +- + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index a3e41cb38ef..9ec837e837d 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6333,5 +6333,5 @@ void CDECL wined3d_device_wait_idle(struct wined3d_device *device) + { + TRACE("device %p.\n", device); + +- wined3d_cs_emit_wait_idle(device->cs); ++ device->cs->c.ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index a01ee1690c1..0a18d909c5e 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +-- +2.30.2 + +From 08d1dd39399a79f3f701cbcb37b733a5f48af5fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 22:04:21 +0200 +Subject: [PATCH 08/12] wined3d: Implement synchronous texture access. + +For vrclient. +--- + dlls/d3d11/texture.c | 15 ++++ + dlls/wined3d/cs.c | 48 ++++++++++++ + dlls/wined3d/device.c | 2 + + dlls/wined3d/texture.c | 123 +++++++++++++++++++++++++++++++ + dlls/wined3d/wined3d_private.h | 15 ++++ + include/wine/wined3d-interop.idl | 2 + + include/wine/wined3d.h | 2 + + 7 files changed, 207 insertions(+) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index c46aae99a24..7f7e8254225 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -741,6 +741,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Textur + wined3d_mutex_unlock(); + } + ++static unsigned int STDMETHODCALLTYPE d3d11_texture2d_get_gl_texture(IWineD3D11Texture2D *iface) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ unsigned int id; ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ id = wined3d_get_gl_texture(texture->wined3d_texture); ++ wined3d_mutex_unlock(); ++ ++ return id; ++} ++ + static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ +@@ -760,6 +774,7 @@ static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetDesc, + /* IWineD3D11Texture methods */ + d3d11_texture2d_access_gl_texture, ++ d3d11_texture2d_get_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 298f74c46c0..511883924a1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -79,6 +79,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_USER_CALLBACK, ++ WINED3D_CS_OP_FENCE, + WINED3D_CS_OP_STOP, + }; + +@@ -482,6 +483,12 @@ struct wined3d_cs_wait_idle + enum wined3d_cs_op opcode; + }; + ++struct wined3d_cs_fence ++{ ++ enum wined3d_cs_op opcode; ++ GLsync *fence; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2803,6 +2810,46 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_fence *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ GLsync fence; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); ++ wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); ++ ++ *op->fence = fence; ++ ++ checkGLcall("fence"); ++ ++ context_release(context); ++} ++ ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_fence *op; ++ GLsync fence; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_FENCE; ++ op->fence = &fence; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++ ++ return fence; ++} ++ ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++{ ++ return wined3d_cs_emit_fence(cs); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2918,6 +2965,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, ++ /* WINED3D_CS_OP_FENCE */ wined3d_cs_exec_fence, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 9ec837e837d..9d042f4d0e1 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -218,6 +218,8 @@ void wined3d_device_cleanup(struct wined3d_device *device) + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + ++ wined3d_destroy_gl_vr_context(&device->vr_context); ++ + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index b8fa5d7d3ae..25c5e57fca0 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4585,6 +4585,129 @@ void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + ++static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) ++{ ++ const struct wined3d_adapter *adapter = device->adapter; ++ const struct wined3d_gl_info *gl_info = &wined3d_adapter_gl_const(adapter)->gl_info; ++ struct wined3d_vr_gl_context *ctx = &device->vr_context; ++ PIXELFORMATDESCRIPTOR pfd; ++ int pixel_format; ++ HGLRC share_ctx; ++ ++ if (ctx->gl_info) ++ return gl_info; ++ ++ TRACE("Creating GL context.\n"); ++ ++ if (!gl_info->p_wglCreateContextAttribsARB) ++ { ++ ERR("wglCreateContextAttribsARB is not supported.\n"); ++ return NULL; ++ } ++ ++ if (!gl_info->supported[ARB_SYNC]) ++ { ++ FIXME("ARB_sync is not supported.\n"); ++ return NULL; ++ } ++ ++ ctx->window = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D VR window", ++ WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); ++ if (!ctx->window) ++ { ++ ERR("Failed to create a window.\n"); ++ return NULL; ++ } ++ ++ ctx->dc = GetDC(ctx->window); ++ if (!ctx->dc) ++ { ++ ERR("Failed to get a DC.\n"); ++ goto fail; ++ } ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd.nSize = sizeof(pfd); ++ pfd.nVersion = 1; ++ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; ++ pfd.iPixelType = PFD_TYPE_RGBA; ++ pfd.cColorBits = 32; ++ pfd.iLayerType = PFD_MAIN_PLANE; ++ ++ if (!(pixel_format = ChoosePixelFormat(ctx->dc, &pfd))) ++ { ++ ERR("Failed to find a suitable pixel format.\n"); ++ goto fail; ++ } ++ DescribePixelFormat(ctx->dc, pixel_format, sizeof(pfd), &pfd); ++ SetPixelFormat(ctx->dc, pixel_format, &pfd); ++ ++ share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; ++ if (!(ctx->gl_ctx = context_create_wgl_attribs(gl_info, ctx->dc, share_ctx))) ++ { ++ WARN("Failed to create GL context for VR.\n"); ++ goto fail; ++ } ++ ++ if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) ++ { ++ ERR("Failed to make GL context current.\n"); ++ goto fail; ++ } ++ ++ checkGLcall("create context"); ++ ++ ctx->gl_info = gl_info; ++ return gl_info; ++ ++fail: ++ if (ctx->gl_ctx) ++ wglDeleteContext(ctx->gl_ctx); ++ ctx->gl_ctx = NULL; ++ if (ctx->dc) ++ ReleaseDC(ctx->window, ctx->dc); ++ ctx->dc = NULL; ++ if (ctx->window) ++ DestroyWindow(ctx->window); ++ ctx->window = NULL; ++ return NULL; ++} ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) ++{ ++ if (!ctx->gl_info) ++ return; ++ ++ TRACE("Destroying GL context.\n"); ++ ++ wglMakeCurrent(NULL, NULL); ++ wglDeleteContext(ctx->gl_ctx); ++ ReleaseDC(ctx->window, ctx->dc); ++ DestroyWindow(ctx->window); ++} ++ ++unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_texture_gl *gl_texture; ++ GLsync fence; ++ ++ TRACE("texture %p.\n", texture); ++ ++ if (!(gl_info = wined3d_prepare_vr_gl_context(device))) ++ return 0; ++ ++ fence = wined3d_cs_synchronize(device->cs); ++ GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); ++ GL_EXTCALL(glDeleteSync(fence)); ++ ++ checkGLcall("synchronize CS"); ++ ++ gl_texture = wined3d_texture_gl(texture); ++ return gl_texture->texture_rgb.name; ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 0a18d909c5e..4bd32d95cda 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry + struct wined3d_stream_output_element elements[1]; + }; + ++struct wined3d_vr_gl_context ++{ ++ HWND window; ++ HDC dc; ++ HGLRC gl_ctx; ++ const struct wined3d_gl_info *gl_info; ++}; ++ + struct wined3d_device + { + LONG ref; +@@ -3842,6 +3850,8 @@ struct wined3d_device + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; ++ ++ struct wined3d_vr_gl_context vr_context; + + CRITICAL_SECTION bo_map_lock; + }; +@@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++ ++ + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index c5395ccc0ed..b1bda8ada76 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -28,6 +28,8 @@ import "d3d11.idl"; + interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ ++ unsigned int get_gl_texture(); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 81235a7677c..d63c54ca593 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,8 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, uns + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + ++unsigned int __cdecl wined3d_get_gl_texture(struct wined3d_texture *texture); ++ + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, +-- +2.30.2 + +From cc1f77d00ee61e8620029c08110015543682acb8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:04:07 +0200 +Subject: [PATCH 09/12] wined3d: Load TEXTURE_RGB location for synchronous + texture access. + +--- + dlls/wined3d/cs.c | 10 +++++++--- + dlls/wined3d/texture.c | 2 +- + dlls/wined3d/wined3d_private.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 511883924a1..6242651dd72 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -486,6 +486,7 @@ struct wined3d_cs_wait_idle + struct wined3d_cs_fence + { + enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; + GLsync *fence; + }; + +@@ -2820,6 +2821,8 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context = context_acquire(cs->c.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + ++ wined3d_texture_load_location(op->texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ + fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); + +@@ -2830,13 +2833,14 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context_release(context); + } + +-static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs, struct wined3d_texture *texture) + { + struct wined3d_cs_fence *op; + GLsync fence; + + op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FENCE; ++ op->texture = texture; + op->fence = &fence; + + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +@@ -2845,9 +2849,9 @@ static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) + return fence; + } + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) + { +- return wined3d_cs_emit_fence(cs); ++ return wined3d_cs_emit_fence(cs, texture); + } + + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 25c5e57fca0..72a64889ca9 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4698,7 +4698,7 @@ unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) + if (!(gl_info = wined3d_prepare_vr_gl_context(device))) + return 0; + +- fence = wined3d_cs_synchronize(device->cs); ++ fence = wined3d_cs_synchronize(device->cs, texture); + GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); + GL_EXTCALL(glDeleteSync(fence)); + +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 4bd32d95cda..2a9b2c08877 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; + + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + +-- +2.30.2 + +From 29f4110ef25083ad84d3f441986345abf79f3eaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:17:15 +0200 +Subject: [PATCH 10/12] d3d11: Pass IWineD3D11Texture2D to access_gl_texture(). + +--- + dlls/d3d11/texture.c | 21 +++++++-------------- + include/wine/wined3d-interop.idl | 3 ++- + 2 files changed, 9 insertions(+), 15 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 7f7e8254225..32bf3c3c9cc 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,27 +716,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) ++ gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { + struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; +- IWineD3D11Texture2D *depth_d3d11 = NULL; ++ struct wined3d_texture *wined3d_depth_texture = NULL; + +- TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", ++ iface, callback, depth_texture, data, size); + + wined3d_mutex_lock(); + +- if (depth_unk) +- { +- HRESULT hr; +- hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); +- if(hr == S_OK) +- depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); +- } +- +- wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ if (depth_texture) ++ wined3d_depth_texture = impl_from_IWineD3D11Texture2D(depth_texture)->wined3d_texture; + +- if (depth_d3d11) +- IWineD3D11Texture2D_Release(depth_d3d11); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, wined3d_depth_texture, data, size); + + wined3d_mutex_unlock(); + } +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index b1bda8ada76..f960e2f6d9d 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -27,7 +27,8 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, ++ IWineD3D11Texture2D *depth_texture, const void *data, unsigned int data_size); + + unsigned int get_gl_texture(); + } +-- +2.30.2 + +From 9a97a24ad5001cc4d25b9b8236fbf13b76d9eaef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 11 Oct 2018 11:31:43 +0200 +Subject: [PATCH 11/12] d3d11: Remove unused 'depth_tex' variable. + +--- + dlls/d3d11/texture.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 32bf3c3c9cc..f6e7d474d99 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -718,7 +718,7 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, + gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_texture *wined3d_depth_texture = NULL; + + TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", +-- +2.30.2 + +From b476c31f53be82ad3b8a3ec9d7987f75e8040cfe Mon Sep 17 00:00:00 2001 +From: GloriousEggroll +Date: Sun, 18 Aug 2019 19:05:52 -0600 +Subject: [PATCH 12/12] wined3d + +--- + dlls/wined3d/wined3d.spec | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec +index 2879e1653d3..bbc9ce7b99c 100644 +--- a/dlls/wined3d/wined3d.spec ++++ b/dlls/wined3d/wined3d.spec +@@ -367,6 +367,11 @@ + @ cdecl wined3d_vertex_declaration_decref(ptr) + @ cdecl wined3d_vertex_declaration_get_parent(ptr) + @ cdecl wined3d_vertex_declaration_incref(ptr) ++ ++@ cdecl wined3d_access_gl_texture(ptr ptr ptr long) ++@ cdecl wined3d_device_run_cs_callback(ptr ptr ptr long) ++@ cdecl wined3d_device_wait_idle(ptr) ++@ cdecl wined3d_get_gl_texture(ptr) + + @ cdecl vkd3d_create_instance(ptr ptr) + @ cdecl vkd3d_instance_decref(ptr) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-890877f.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-890877f.patch new file mode 100644 index 000000000..9948ea861 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-890877f.patch @@ -0,0 +1,1622 @@ +From 4ad17bc8a5651c65b7e1bced3c9e04bce9f77c91 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 01/12] wined3d: Implement GL texture access callbacks. + +--- + dlls/wined3d/cs.c | 52 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/texture.c | 10 +++++++ + dlls/wined3d/wined3d_private.h | 3 ++ + include/wine/wined3d.h | 5 ++++ + 4 files changed, 70 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 21aaa2e54b6..e291e2b550d 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -77,6 +77,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, ++ WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -457,6 +458,15 @@ struct wined3d_cs_generate_mipmaps + struct wined3d_shader_resource_view *view; + }; + ++struct wined3d_cs_gl_texture_callback ++{ ++ enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; ++ wined3d_gl_texture_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2698,6 +2708,44 @@ void wined3d_device_context_emit_generate_mipmaps(struct wined3d_device_context + wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_gl_texture_callback *op = data; ++ struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ ++ op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ ++ context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); ++ context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); ++ ++ checkGLcall("texture callback\n"); ++ ++ context_release(context); ++ ++} ++ ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_gl_texture_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; ++ op->texture = texture; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2865,6 +2916,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, ++ /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 39e6e2a68b7..bdcd0b55ae6 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4574,6 +4574,16 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); + } + ++void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ ++ TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index f5b1a078907..756d47ef20d 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4762,6 +4762,9 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + cs->c.ops->finish(&cs->c, queue_id); + } + ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index dd1c15f14ed..2123e857a52 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,6 +2969,11 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++ ++void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 34565d56b8cfcdcf787c5a3a0466f990918d1139 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 02/12] wined3d: Implement command stream callbacks. + +--- + dlls/wined3d/cs.c | 40 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/device.c | 8 +++++++ + dlls/wined3d/wined3d_private.h | 2 ++ + include/wine/wined3d.h | 5 +++++ + 4 files changed, 55 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index e291e2b550d..cf55385c3c1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -78,6 +78,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, ++ WINED3D_CS_OP_USER_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -467,6 +468,14 @@ struct wined3d_cs_gl_texture_callback + BYTE data[1]; + }; + ++struct wined3d_cs_user_callback ++{ ++ enum wined3d_cs_op opcode; ++ wined3d_cs_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2749,6 +2758,36 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_user_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_user_callback *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ op->callback(op->data, op->data_size); ++ ++ checkGLcall("user callback\n"); ++ ++ context_release(context); ++} ++ ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_user_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_USER_CALLBACK; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2893,6 +2932,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, ++ /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 484130b3103..286d4d3acf5 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6320,3 +6320,11 @@ LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL + else + return CallWindowProcA(proc, window, message, wparam, lparam); + } ++ ++void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ TRACE("device %p, callback %p, data %p, size %u.\n", device, callback, data, size); ++ ++ wined3d_cs_emit_user_callback(device->cs, callback, data, size); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 756d47ef20d..602647b1e94 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 2123e857a52..39689bdb4e1 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,11 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, con + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size); + ++typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); ++ ++void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 434e12794d7dda50554dbcf781af282816d39121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 03/12] d3d11: Add IWineD3D11Texture2D interface. + +--- + dlls/d3d11/d3d11_private.h | 4 +- + dlls/d3d11/device.c | 4 +- + dlls/d3d11/texture.c | 65 +++++++++++++++++++++----------- + include/Makefile.in | 1 + + include/wine/wined3d-interop.idl | 31 +++++++++++++++ + 5 files changed, 80 insertions(+), 25 deletions(-) + create mode 100644 include/wine/wined3d-interop.idl + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 1a8cdc6d77c..2b1809726ca 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -39,6 +39,8 @@ + #include "wine/winedxgi.h" + #include "wine/rbtree.h" + ++#include "wine/wined3d-interop.h" ++ + #define MAKE_TAG(ch0, ch1, ch2, ch3) \ + ((DWORD)(ch0) | ((DWORD)(ch1) << 8) | \ + ((DWORD)(ch2) << 16) | ((DWORD)(ch3) << 24 )) +@@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D + /* ID3D11Texture2D, ID3D10Texture2D */ + struct d3d_texture2d + { +- ID3D11Texture2D ID3D11Texture2D_iface; ++ IWineD3D11Texture2D ID3D11Texture2D_iface; + ID3D10Texture2D ID3D10Texture2D_iface; + LONG refcount; + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 0819918b2a1..41967dd0a1c 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3165,7 +3165,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_device_CreateTexture2D(ID3D11Device2 *ifa + if (FAILED(hr = d3d_texture2d_create(device, desc, data, &object))) + return hr; + +- *texture = &object->ID3D11Texture2D_iface; ++ *texture = (ID3D11Texture2D *)&object->ID3D11Texture2D_iface; + + return S_OK; + } +@@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic + + *wined3d_texture = texture->wined3d_texture; + wined3d_texture_incref(*wined3d_texture); +- ID3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); + + return S_OK; + } +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 061bbb09795..dd21c6d7f08 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -518,14 +518,21 @@ HRESULT d3d_texture1d_create(struct d3d_device *device, const D3D11_TEXTURE1D_DE + + /* ID3D11Texture2D methods */ + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D *iface, REFIID riid, void **object) ++static inline struct d3d_texture2d *impl_from_IWineD3D11Texture2D(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); +- HRESULT hr; ++ return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(IWineD3D11Texture2D *iface, ++ REFIID riid, void **object) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ HRESULT hr; + + TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); + +- if (IsEqualGUID(riid, &IID_ID3D11Texture2D) ++ if (IsEqualGUID(riid, &IID_IWineD3D11Texture2D) ++ || IsEqualGUID(riid, &IID_ID3D11Texture2D) + || IsEqualGUID(riid, &IID_ID3D11Resource) + || IsEqualGUID(riid, &IID_ID3D11DeviceChild) + || IsEqualGUID(riid, &IID_IUnknown)) +@@ -554,9 +561,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedIncrement(&texture->refcount); + + TRACE("%p increasing refcount to %u.\n", texture, refcount); +@@ -572,9 +579,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedDecrement(&texture->refcount); + + TRACE("%p decreasing refcount to %u.\n", texture, refcount); +@@ -594,9 +601,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) + return refcount; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, ID3D11Device **device) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(IWineD3D11Texture2D *iface, ID3D11Device **device) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + + TRACE("iface %p, device %p.\n", iface, device); + +@@ -604,10 +611,10 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, + ID3D11Device_AddRef(*device); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT *data_size, void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -624,10 +631,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D + return d3d_get_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT data_size, const void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -644,10 +651,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D + return d3d_set_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(IWineD3D11Texture2D *iface, + REFGUID guid, const IUnknown *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -664,7 +671,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11T + return d3d_set_private_data_interface(&texture->private_store, guid, data); + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, ++static void STDMETHODCALLTYPE d3d11_texture2d_GetType(IWineD3D11Texture2D *iface, + D3D11_RESOURCE_DIMENSION *resource_dimension) + { + TRACE("iface %p, resource_dimension %p.\n", iface, resource_dimension); +@@ -672,21 +679,21 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, + *resource_dimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(ID3D11Texture2D *iface, UINT eviction_priority) ++static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(IWineD3D11Texture2D *iface, UINT eviction_priority) + { + FIXME("iface %p, eviction_priority %#x stub!\n", iface, eviction_priority); + } + +-static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(ID3D11Texture2D *iface) ++static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(IWineD3D11Texture2D *iface) + { + FIXME("iface %p stub!\n", iface); + + return 0; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_resource_desc wined3d_desc; + + TRACE("iface %p, desc %p.\n", iface, desc); +@@ -708,7 +715,19 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3 + desc->SampleDesc.Quality = wined3d_desc.multisample_quality; + } + +-static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = ++static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, ++ gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ ++ TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ ++ wined3d_mutex_lock(); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ + d3d11_texture2d_QueryInterface, +@@ -725,13 +744,15 @@ static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetEvictionPriority, + /* ID3D11Texture2D methods */ + d3d11_texture2d_GetDesc, ++ /* IWineD3D11Texture methods */ ++ d3d11_texture2d_access_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) + { + if (!iface) + return NULL; +- assert(iface->lpVtbl == &d3d11_texture2d_vtbl); ++ assert(iface->lpVtbl == (void *)&d3d11_texture2d_vtbl); + return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); + } + +diff --git a/include/Makefile.in b/include/Makefile.in +index 9822bce6bdd..d9b646f7de1 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -770,6 +770,7 @@ SOURCES = \ + wine/winbase16.h \ + wine/windef16.h \ + wine/wine_common_ver.rc \ ++ wine/wined3d-interop.idl \ + wine/wined3d.h \ + wine/winedxgi.idl \ + wine/wingdi16.h \ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +new file mode 100644 +index 00000000000..884baf958c2 +--- /dev/null ++++ b/include/wine/wined3d-interop.idl +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2018 JĂ³zef Kucia 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 ++ */ ++ ++#if 0 ++#pragma makedep header install ++#endif ++ ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++ ++import "d3d11.idl"; ++ ++[ ++ object, ++ local, ++ uuid(267dc993-d15e-4015-aaac-b7559e226cc3) ++] ++interface IWineD3D11Texture2D : ID3D11Texture2D ++{ ++ void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From 17c027811a6641eea3298f5cb24c69c796dca328 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 04/12] d3d11: Add IWineD3D11Device interface. + +--- + dlls/d3d11/d3d11_private.h | 1 + + dlls/d3d11/device.c | 52 ++++++++++++++++++++++++++++++++ + include/wine/wined3d-interop.idl | 12 ++++++++ + 3 files changed, 65 insertions(+) + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 2b1809726ca..ec0bd79fdd5 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -564,6 +564,7 @@ struct d3d_device + ID3D10Multithread ID3D10Multithread_iface; + IWineDXGIDeviceParent IWineDXGIDeviceParent_iface; + IUnknown *outer_unk; ++ IWineD3D11Device IWineD3D11Device_iface; + LONG refcount; + + BOOL d3d11_only; +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 41967dd0a1c..887e7de00e9 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3119,6 +3119,53 @@ static ULONG STDMETHODCALLTYPE d3d11_device_Release(ID3D11Device2 *iface) + return IUnknown_Release(device->outer_unk); + } + ++/* IWineD3D11Device methods */ ++ ++static inline struct d3d_device *impl_from_IWineD3D11Device(IWineD3D11Device *iface) ++{ ++ return CONTAINING_RECORD(iface, struct d3d_device, IWineD3D11Device_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE wine_device_QueryInterface(IWineD3D11Device *iface, REFIID riid, void **out) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_QueryInterface(device->outer_unk, riid, out); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_AddRef(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_AddRef(device->outer_unk); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_Release(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_Release(device->outer_unk); ++} ++ ++static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device *iface, ++ user_cs_callback callback, const void *data, unsigned int data_size) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p, callback %p, data %p, data_size %u.\n", iface, callback, data, data_size); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_run_cs_callback(device->wined3d_device, callback, data, data_size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11DeviceVtbl wine_device_vtbl = ++{ ++ /* IUnknown methods */ ++ wine_device_QueryInterface, ++ wine_device_AddRef, ++ wine_device_Release, ++ /* IWineD3D11Device methods */ ++ wine_device_run_on_command_stream, ++}; ++ + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, + const D3D11_SUBRESOURCE_DATA *data, ID3D11Buffer **buffer) + { +@@ -4264,6 +4311,10 @@ static HRESULT STDMETHODCALLTYPE d3d_device_inner_QueryInterface(IUnknown *iface + { + *out = &device->IWineDXGIDeviceParent_iface; + } ++ else if (IsEqualGUID(riid, &IID_IWineD3D11Device)) ++ { ++ *out = &device->IWineD3D11Device_iface; ++ } + else + { + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); +@@ -6648,6 +6699,7 @@ void d3d_device_init(struct d3d_device *device, void *outer_unknown) + device->ID3D10Device1_iface.lpVtbl = &d3d10_device1_vtbl; + device->ID3D10Multithread_iface.lpVtbl = &d3d10_multithread_vtbl; + device->IWineDXGIDeviceParent_iface.lpVtbl = &d3d_dxgi_device_parent_vtbl; ++ device->IWineD3D11Device_iface.lpVtbl = &wine_device_vtbl; + device->device_parent.ops = &d3d_wined3d_device_parent_ops; + device->refcount = 1; + /* COM aggregation always takes place */ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 884baf958c2..d5c91623b3c 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -29,3 +29,15 @@ interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); + } ++ ++typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); ++ ++[ ++ object, ++ local, ++ uuid(8f02de7e-d55d-457b-9423-83456e49c58a) ++] ++interface IWineD3D11Device : IUnknown ++{ ++ void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From af8a3f1e0c505ecb0a2f6a899989431e85013f74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 19 Apr 2018 14:04:49 +0200 +Subject: [PATCH 05/12] wined3d: Implement wined3d_device_wait_idle(). + +--- + dlls/d3d11/device.c | 12 ++++++++++++ + dlls/wined3d/cs.c | 20 ++++++++++++++++++++ + dlls/wined3d/device.c | 7 +++++++ + dlls/wined3d/wined3d_private.h | 1 + + include/wine/wined3d-interop.idl | 2 ++ + include/wine/wined3d.h | 1 + + 6 files changed, 43 insertions(+) + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 887e7de00e9..1ff3f32f2c2 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3156,6 +3156,17 @@ static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device + wined3d_mutex_unlock(); + } + ++static void STDMETHODCALLTYPE wine_device_wait_idle(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_wait_idle(device->wined3d_device); ++ wined3d_mutex_unlock(); ++} ++ + static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + { + /* IUnknown methods */ +@@ -3164,6 +3175,7 @@ static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + wine_device_Release, + /* IWineD3D11Device methods */ + wine_device_run_on_command_stream, ++ wine_device_wait_idle, + }; + + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index cf55385c3c1..0eeabdb6640 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -476,6 +477,11 @@ struct wined3d_cs_user_callback + BYTE data[1]; + }; + ++struct wined3d_cs_wait_idle ++{ ++ enum wined3d_cs_op opcode; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2788,6 +2794,19 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} ++ ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_wait_idle *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_WAIT_IDLE; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 286d4d3acf5..a3e41cb38ef 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6328,3 +6328,10 @@ void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, + + wined3d_cs_emit_user_callback(device->cs, callback, data, size); + } ++ ++void CDECL wined3d_device_wait_idle(struct wined3d_device *device) ++{ ++ TRACE("device %p.\n", device); ++ ++ wined3d_cs_emit_wait_idle(device->cs); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 602647b1e94..8a38f6d4925 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index d5c91623b3c..6f8ea3770e3 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -40,4 +40,6 @@ typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_siz + interface IWineD3D11Device : IUnknown + { + void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++ ++ void wait_idle(); + } +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 39689bdb4e1..c90a71dbd5b 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2978,6 +2978,7 @@ typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size) + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, + wined3d_cs_callback callback, const void *data, unsigned int size); ++void __cdecl wined3d_device_wait_idle(struct wined3d_device *device); + + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) +-- +2.30.2 + +From 9c84c7ec7714dc3f0f9038b60735788f9d37d77c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 1 May 2018 15:06:30 -0500 +Subject: [PATCH 06/12] wined3d: Support retrieving depth texture in GL texture + callback + +--- + dlls/d3d11/texture.c | 20 +++++++++++++++++--- + dlls/wined3d/cs.c | 14 ++++++++++++-- + dlls/wined3d/texture.c | 7 ++++--- + dlls/wined3d/wined3d_private.h | 3 ++- + include/wine/wined3d-interop.idl | 4 ++-- + include/wine/wined3d.h | 4 ++-- + 6 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index dd21c6d7f08..c46aae99a24 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,14 +716,28 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, const void *data, unsigned int size) ++ gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ IWineD3D11Texture2D *depth_d3d11 = NULL; + + TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); + + wined3d_mutex_lock(); +- wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ ++ if (depth_unk) ++ { ++ HRESULT hr; ++ hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); ++ if(hr == S_OK) ++ depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); ++ } ++ ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ ++ if (depth_d3d11) ++ IWineD3D11Texture2D_Release(depth_d3d11); ++ + wined3d_mutex_unlock(); + } + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 0eeabdb6640..edfdeecee3c 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -464,6 +464,7 @@ struct wined3d_cs_gl_texture_callback + { + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; ++ struct wined3d_texture *depth_texture; + wined3d_gl_texture_callback callback; + unsigned int data_size; + BYTE data[1]; +@@ -2727,6 +2728,7 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + { + const struct wined3d_cs_gl_texture_callback *op = data; + struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ struct wined3d_texture_gl *depth_texture = wined3d_texture_gl(op->depth_texture); + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + +@@ -2734,8 +2736,12 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + gl_info = wined3d_context_gl(context)->gl_info; + + wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ if (depth_texture) ++ wined3d_texture_load_location(&depth_texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + +- op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ op->callback(texture->texture_rgb.name, ++ depth_texture ? depth_texture->texture_rgb.name : 0, ++ op->data, op->data_size); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +@@ -2750,13 +2750,15 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_cs_gl_texture_callback *op; + + op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; + op->texture = texture; ++ op->depth_texture = depth_texture; + op->callback = callback; + op->data_size = size; + memcpy(op->data, data, size); +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index bdcd0b55ae6..b8fa5d7d3ae 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4575,13 +4575,14 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + } + + void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_device *device = texture->resource.device; + +- TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ TRACE("texture %p, depth_texture %p, callback %p, data %p, size %u.\n", texture, depth_texture, callback, data, size); + +- wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 8a38f6d4925..a01ee1690c1 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4763,7 +4763,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 6f8ea3770e3..c5395ccc0ed 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -16,7 +16,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +-typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size); + + import "d3d11.idl"; + +@@ -27,7 +27,7 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index c90a71dbd5b..81235a7677c 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,10 +2969,10 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + +-typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int size); + + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + +-- +2.30.2 + +From d00f400da6cc085c207d0501944804ad63cb486b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 23:10:14 +0200 +Subject: [PATCH 07/12] wined3d: Get rid of wined3d_cs_emit_wait_idle(). + +--- + dlls/wined3d/cs.c | 15 --------------- + dlls/wined3d/device.c | 2 +- + dlls/wined3d/wined3d_private.h | 1 - + 3 files changed, 1 insertion(+), 17 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index edfdeecee3c..298f74c46c0 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -2804,19 +2803,6 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + +-static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} +- +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) +-{ +- struct wined3d_cs_wait_idle *op; +- +- op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); +- op->opcode = WINED3D_CS_OP_WAIT_IDLE; +- +- cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +- cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); +-} +- + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index a3e41cb38ef..9ec837e837d 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6333,5 +6333,5 @@ void CDECL wined3d_device_wait_idle(struct wined3d_device *device) + { + TRACE("device %p.\n", device); + +- wined3d_cs_emit_wait_idle(device->cs); ++ device->cs->c.ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index a01ee1690c1..0a18d909c5e 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +-- +2.30.2 + +From 08d1dd39399a79f3f701cbcb37b733a5f48af5fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 22:04:21 +0200 +Subject: [PATCH 08/12] wined3d: Implement synchronous texture access. + +For vrclient. +--- + dlls/d3d11/texture.c | 15 ++++ + dlls/wined3d/cs.c | 48 ++++++++++++ + dlls/wined3d/device.c | 2 + + dlls/wined3d/texture.c | 123 +++++++++++++++++++++++++++++++ + dlls/wined3d/wined3d_private.h | 15 ++++ + include/wine/wined3d-interop.idl | 2 + + include/wine/wined3d.h | 2 + + 7 files changed, 207 insertions(+) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index c46aae99a24..7f7e8254225 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -741,6 +741,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Textur + wined3d_mutex_unlock(); + } + ++static unsigned int STDMETHODCALLTYPE d3d11_texture2d_get_gl_texture(IWineD3D11Texture2D *iface) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ unsigned int id; ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ id = wined3d_get_gl_texture(texture->wined3d_texture); ++ wined3d_mutex_unlock(); ++ ++ return id; ++} ++ + static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ +@@ -760,6 +774,7 @@ static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetDesc, + /* IWineD3D11Texture methods */ + d3d11_texture2d_access_gl_texture, ++ d3d11_texture2d_get_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 298f74c46c0..511883924a1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -79,6 +79,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_USER_CALLBACK, ++ WINED3D_CS_OP_FENCE, + WINED3D_CS_OP_STOP, + }; + +@@ -482,6 +483,12 @@ struct wined3d_cs_wait_idle + enum wined3d_cs_op opcode; + }; + ++struct wined3d_cs_fence ++{ ++ enum wined3d_cs_op opcode; ++ GLsync *fence; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2803,6 +2810,46 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_fence *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ GLsync fence; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); ++ wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); ++ ++ *op->fence = fence; ++ ++ checkGLcall("fence"); ++ ++ context_release(context); ++} ++ ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_fence *op; ++ GLsync fence; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_FENCE; ++ op->fence = &fence; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++ ++ return fence; ++} ++ ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++{ ++ return wined3d_cs_emit_fence(cs); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2918,6 +2965,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, ++ /* WINED3D_CS_OP_FENCE */ wined3d_cs_exec_fence, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 9ec837e837d..9d042f4d0e1 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -218,6 +218,8 @@ void wined3d_device_cleanup(struct wined3d_device *device) + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + ++ wined3d_destroy_gl_vr_context(&device->vr_context); ++ + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index b8fa5d7d3ae..25c5e57fca0 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4585,6 +4585,129 @@ void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + ++static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) ++{ ++ const struct wined3d_adapter *adapter = device->adapter; ++ const struct wined3d_gl_info *gl_info = &adapter->gl_info; ++ struct wined3d_vr_gl_context *ctx = &device->vr_context; ++ PIXELFORMATDESCRIPTOR pfd; ++ int pixel_format; ++ HGLRC share_ctx; ++ ++ if (ctx->gl_info) ++ return gl_info; ++ ++ TRACE("Creating GL context.\n"); ++ ++ if (!gl_info->p_wglCreateContextAttribsARB) ++ { ++ ERR("wglCreateContextAttribsARB is not supported.\n"); ++ return NULL; ++ } ++ ++ if (!gl_info->supported[ARB_SYNC]) ++ { ++ FIXME("ARB_sync is not supported.\n"); ++ return NULL; ++ } ++ ++ ctx->window = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D VR window", ++ WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); ++ if (!ctx->window) ++ { ++ ERR("Failed to create a window.\n"); ++ return NULL; ++ } ++ ++ ctx->dc = GetDC(ctx->window); ++ if (!ctx->dc) ++ { ++ ERR("Failed to get a DC.\n"); ++ goto fail; ++ } ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd.nSize = sizeof(pfd); ++ pfd.nVersion = 1; ++ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; ++ pfd.iPixelType = PFD_TYPE_RGBA; ++ pfd.cColorBits = 32; ++ pfd.iLayerType = PFD_MAIN_PLANE; ++ ++ if (!(pixel_format = ChoosePixelFormat(ctx->dc, &pfd))) ++ { ++ ERR("Failed to find a suitable pixel format.\n"); ++ goto fail; ++ } ++ DescribePixelFormat(ctx->dc, pixel_format, sizeof(pfd), &pfd); ++ SetPixelFormat(ctx->dc, pixel_format, &pfd); ++ ++ share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; ++ if (!(ctx->gl_ctx = context_create_wgl_attribs(gl_info, ctx->dc, share_ctx))) ++ { ++ WARN("Failed to create GL context for VR.\n"); ++ goto fail; ++ } ++ ++ if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) ++ { ++ ERR("Failed to make GL context current.\n"); ++ goto fail; ++ } ++ ++ checkGLcall("create context"); ++ ++ ctx->gl_info = gl_info; ++ return gl_info; ++ ++fail: ++ if (ctx->gl_ctx) ++ wglDeleteContext(ctx->gl_ctx); ++ ctx->gl_ctx = NULL; ++ if (ctx->dc) ++ ReleaseDC(ctx->window, ctx->dc); ++ ctx->dc = NULL; ++ if (ctx->window) ++ DestroyWindow(ctx->window); ++ ctx->window = NULL; ++ return NULL; ++} ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) ++{ ++ if (!ctx->gl_info) ++ return; ++ ++ TRACE("Destroying GL context.\n"); ++ ++ wglMakeCurrent(NULL, NULL); ++ wglDeleteContext(ctx->gl_ctx); ++ ReleaseDC(ctx->window, ctx->dc); ++ DestroyWindow(ctx->window); ++} ++ ++unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_texture_gl *gl_texture; ++ GLsync fence; ++ ++ TRACE("texture %p.\n", texture); ++ ++ if (!(gl_info = wined3d_prepare_vr_gl_context(device))) ++ return 0; ++ ++ fence = wined3d_cs_synchronize(device->cs); ++ GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); ++ GL_EXTCALL(glDeleteSync(fence)); ++ ++ checkGLcall("synchronize CS"); ++ ++ gl_texture = wined3d_texture_gl(texture); ++ return gl_texture->texture_rgb.name; ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 0a18d909c5e..4bd32d95cda 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry + struct wined3d_stream_output_element elements[1]; + }; + ++struct wined3d_vr_gl_context ++{ ++ HWND window; ++ HDC dc; ++ HGLRC gl_ctx; ++ const struct wined3d_gl_info *gl_info; ++}; ++ + struct wined3d_device + { + LONG ref; +@@ -3842,6 +3850,8 @@ struct wined3d_device + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; ++ ++ struct wined3d_vr_gl_context vr_context; + + CRITICAL_SECTION bo_map_lock; + }; +@@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++ ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index c5395ccc0ed..b1bda8ada76 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -28,6 +28,8 @@ import "d3d11.idl"; + interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ ++ unsigned int get_gl_texture(); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 81235a7677c..d63c54ca593 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,8 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, uns + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + ++unsigned int __cdecl wined3d_get_gl_texture(struct wined3d_texture *texture); ++ + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, +-- +2.30.2 + +From cc1f77d00ee61e8620029c08110015543682acb8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:04:07 +0200 +Subject: [PATCH 09/12] wined3d: Load TEXTURE_RGB location for synchronous + texture access. + +--- + dlls/wined3d/cs.c | 10 +++++++--- + dlls/wined3d/texture.c | 2 +- + dlls/wined3d/wined3d_private.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 511883924a1..6242651dd72 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -486,6 +486,7 @@ struct wined3d_cs_wait_idle + struct wined3d_cs_fence + { + enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; + GLsync *fence; + }; + +@@ -2820,6 +2821,8 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context = context_acquire(cs->c.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + ++ wined3d_texture_load_location(op->texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ + fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); + +@@ -2830,13 +2833,14 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context_release(context); + } + +-static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs, struct wined3d_texture *texture) + { + struct wined3d_cs_fence *op; + GLsync fence; + + op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FENCE; ++ op->texture = texture; + op->fence = &fence; + + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +@@ -2845,9 +2849,9 @@ static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) + return fence; + } + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) + { +- return wined3d_cs_emit_fence(cs); ++ return wined3d_cs_emit_fence(cs, texture); + } + + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 25c5e57fca0..72a64889ca9 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4698,7 +4698,7 @@ unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) + if (!(gl_info = wined3d_prepare_vr_gl_context(device))) + return 0; + +- fence = wined3d_cs_synchronize(device->cs); ++ fence = wined3d_cs_synchronize(device->cs, texture); + GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); + GL_EXTCALL(glDeleteSync(fence)); + +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 4bd32d95cda..2a9b2c08877 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; + + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + +-- +2.30.2 + +From 29f4110ef25083ad84d3f441986345abf79f3eaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:17:15 +0200 +Subject: [PATCH 10/12] d3d11: Pass IWineD3D11Texture2D to access_gl_texture(). + +--- + dlls/d3d11/texture.c | 21 +++++++-------------- + include/wine/wined3d-interop.idl | 3 ++- + 2 files changed, 9 insertions(+), 15 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 7f7e8254225..32bf3c3c9cc 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,27 +716,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) ++ gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { + struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; +- IWineD3D11Texture2D *depth_d3d11 = NULL; ++ struct wined3d_texture *wined3d_depth_texture = NULL; + +- TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", ++ iface, callback, depth_texture, data, size); + + wined3d_mutex_lock(); + +- if (depth_unk) +- { +- HRESULT hr; +- hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); +- if(hr == S_OK) +- depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); +- } +- +- wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ if (depth_texture) ++ wined3d_depth_texture = impl_from_IWineD3D11Texture2D(depth_texture)->wined3d_texture; + +- if (depth_d3d11) +- IWineD3D11Texture2D_Release(depth_d3d11); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, wined3d_depth_texture, data, size); + + wined3d_mutex_unlock(); + } +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index b1bda8ada76..f960e2f6d9d 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -27,7 +27,8 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, ++ IWineD3D11Texture2D *depth_texture, const void *data, unsigned int data_size); + + unsigned int get_gl_texture(); + } +-- +2.30.2 + +From 9a97a24ad5001cc4d25b9b8236fbf13b76d9eaef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 11 Oct 2018 11:31:43 +0200 +Subject: [PATCH 11/12] d3d11: Remove unused 'depth_tex' variable. + +--- + dlls/d3d11/texture.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 32bf3c3c9cc..f6e7d474d99 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -718,7 +718,7 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, + gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_texture *wined3d_depth_texture = NULL; + + TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", +-- +2.30.2 + +From b476c31f53be82ad3b8a3ec9d7987f75e8040cfe Mon Sep 17 00:00:00 2001 +From: GloriousEggroll +Date: Sun, 18 Aug 2019 19:05:52 -0600 +Subject: [PATCH 12/12] wined3d + +--- + dlls/wined3d/wined3d.spec | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec +index 2879e1653d3..bbc9ce7b99c 100644 +--- a/dlls/wined3d/wined3d.spec ++++ b/dlls/wined3d/wined3d.spec +@@ -367,6 +367,11 @@ + @ cdecl wined3d_vertex_declaration_decref(ptr) + @ cdecl wined3d_vertex_declaration_get_parent(ptr) + @ cdecl wined3d_vertex_declaration_incref(ptr) ++ ++@ cdecl wined3d_access_gl_texture(ptr ptr ptr long) ++@ cdecl wined3d_device_run_cs_callback(ptr ptr ptr long) ++@ cdecl wined3d_device_wait_idle(ptr) ++@ cdecl wined3d_get_gl_texture(ptr) + + @ cdecl vkd3d_create_instance(ptr ptr) + @ cdecl vkd3d_instance_decref(ptr) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-8f5aa33.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-8f5aa33.patch new file mode 100644 index 000000000..0ea9c5536 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-8f5aa33.patch @@ -0,0 +1,1630 @@ +From 4ad17bc8a5651c65b7e1bced3c9e04bce9f77c91 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 01/12] wined3d: Implement GL texture access callbacks. + +--- + dlls/wined3d/cs.c | 52 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/texture.c | 10 +++++++ + dlls/wined3d/wined3d_private.h | 3 ++ + include/wine/wined3d.h | 5 ++++ + 4 files changed, 70 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 21aaa2e54b6..e291e2b550d 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -77,6 +77,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, ++ WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -457,6 +458,15 @@ struct wined3d_cs_generate_mipmaps + struct wined3d_shader_resource_view *view; + }; + ++struct wined3d_cs_gl_texture_callback ++{ ++ enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; ++ wined3d_gl_texture_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2698,6 +2708,44 @@ void wined3d_device_context_emit_generate_mipmaps(struct wined3d_device_context + wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_gl_texture_callback *op = data; ++ struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ ++ op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ ++ context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); ++ context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); ++ ++ checkGLcall("texture callback\n"); ++ ++ context_release(context); ++ ++} ++ ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_gl_texture_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; ++ op->texture = texture; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2865,6 +2916,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, ++ /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 39e6e2a68b7..bdcd0b55ae6 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4574,6 +4574,16 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); + } + ++void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ ++ TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index f5b1a078907..756d47ef20d 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4762,6 +4762,9 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + cs->c.ops->finish(&cs->c, queue_id); + } + ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index dd1c15f14ed..2123e857a52 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,6 +2969,11 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++ ++void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 34565d56b8cfcdcf787c5a3a0466f990918d1139 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 02/12] wined3d: Implement command stream callbacks. + +--- + dlls/wined3d/cs.c | 40 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/device.c | 8 +++++++ + dlls/wined3d/wined3d_private.h | 2 ++ + include/wine/wined3d.h | 5 +++++ + 4 files changed, 55 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index e291e2b550d..cf55385c3c1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -78,6 +78,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, ++ WINED3D_CS_OP_USER_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -467,6 +468,14 @@ struct wined3d_cs_gl_texture_callback + BYTE data[1]; + }; + ++struct wined3d_cs_user_callback ++{ ++ enum wined3d_cs_op opcode; ++ wined3d_cs_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2749,6 +2758,36 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_user_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_user_callback *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ op->callback(op->data, op->data_size); ++ ++ checkGLcall("user callback\n"); ++ ++ context_release(context); ++} ++ ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_user_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_USER_CALLBACK; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2893,6 +2932,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, ++ /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 484130b3103..286d4d3acf5 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6320,3 +6320,11 @@ LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL + else + return CallWindowProcA(proc, window, message, wparam, lparam); + } ++ ++void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ TRACE("device %p, callback %p, data %p, size %u.\n", device, callback, data, size); ++ ++ wined3d_cs_emit_user_callback(device->cs, callback, data, size); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 756d47ef20d..602647b1e94 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 2123e857a52..39689bdb4e1 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,11 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, con + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size); + ++typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); ++ ++void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 434e12794d7dda50554dbcf781af282816d39121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 03/12] d3d11: Add IWineD3D11Texture2D interface. + +--- + dlls/d3d11/d3d11_private.h | 4 +- + dlls/d3d11/device.c | 4 +- + dlls/d3d11/texture.c | 65 +++++++++++++++++++++----------- + include/Makefile.in | 1 + + include/wine/wined3d-interop.idl | 31 +++++++++++++++ + 5 files changed, 80 insertions(+), 25 deletions(-) + create mode 100644 include/wine/wined3d-interop.idl + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 1a8cdc6d77c..2b1809726ca 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -39,6 +39,8 @@ + #include "wine/winedxgi.h" + #include "wine/rbtree.h" + ++#include "wine/wined3d-interop.h" ++ + struct d3d_device; + + /* TRACE helper functions */ +@@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D + /* ID3D11Texture2D, ID3D10Texture2D */ + struct d3d_texture2d + { +- ID3D11Texture2D ID3D11Texture2D_iface; ++ IWineD3D11Texture2D ID3D11Texture2D_iface; + ID3D10Texture2D ID3D10Texture2D_iface; + LONG refcount; + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 0819918b2a1..41967dd0a1c 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3165,7 +3165,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_device_CreateTexture2D(ID3D11Device2 *ifa + if (FAILED(hr = d3d_texture2d_create(device, desc, data, &object))) + return hr; + +- *texture = &object->ID3D11Texture2D_iface; ++ *texture = (ID3D11Texture2D *)&object->ID3D11Texture2D_iface; + + return S_OK; + } +@@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic + return hr; + + hr = IUnknown_QueryInterface(object->dxgi_resource, &IID_IDXGISurface, (void **)ret_surface); +- ID3D11Texture2D_Release(&object->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&object->ID3D11Texture2D_iface); + return hr; + } + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 061bbb09795..dd21c6d7f08 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -518,14 +518,21 @@ HRESULT d3d_texture1d_create(struct d3d_device *device, const D3D11_TEXTURE1D_DE + + /* ID3D11Texture2D methods */ + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D *iface, REFIID riid, void **object) ++static inline struct d3d_texture2d *impl_from_IWineD3D11Texture2D(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); +- HRESULT hr; ++ return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(IWineD3D11Texture2D *iface, ++ REFIID riid, void **object) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ HRESULT hr; + + TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); + +- if (IsEqualGUID(riid, &IID_ID3D11Texture2D) ++ if (IsEqualGUID(riid, &IID_IWineD3D11Texture2D) ++ || IsEqualGUID(riid, &IID_ID3D11Texture2D) + || IsEqualGUID(riid, &IID_ID3D11Resource) + || IsEqualGUID(riid, &IID_ID3D11DeviceChild) + || IsEqualGUID(riid, &IID_IUnknown)) +@@ -554,9 +561,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedIncrement(&texture->refcount); + + TRACE("%p increasing refcount to %u.\n", texture, refcount); +@@ -572,9 +579,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedDecrement(&texture->refcount); + + TRACE("%p decreasing refcount to %u.\n", texture, refcount); +@@ -594,9 +601,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) + return refcount; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, ID3D11Device **device) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(IWineD3D11Texture2D *iface, ID3D11Device **device) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + + TRACE("iface %p, device %p.\n", iface, device); + +@@ -604,10 +611,10 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, + ID3D11Device_AddRef(*device); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT *data_size, void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -624,10 +631,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D + return d3d_get_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT data_size, const void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -644,10 +651,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D + return d3d_set_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(IWineD3D11Texture2D *iface, + REFGUID guid, const IUnknown *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -664,7 +671,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11T + return d3d_set_private_data_interface(&texture->private_store, guid, data); + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, ++static void STDMETHODCALLTYPE d3d11_texture2d_GetType(IWineD3D11Texture2D *iface, + D3D11_RESOURCE_DIMENSION *resource_dimension) + { + TRACE("iface %p, resource_dimension %p.\n", iface, resource_dimension); +@@ -672,21 +679,21 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, + *resource_dimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(ID3D11Texture2D *iface, UINT eviction_priority) ++static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(IWineD3D11Texture2D *iface, UINT eviction_priority) + { + FIXME("iface %p, eviction_priority %#x stub!\n", iface, eviction_priority); + } + +-static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(ID3D11Texture2D *iface) ++static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(IWineD3D11Texture2D *iface) + { + FIXME("iface %p stub!\n", iface); + + return 0; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_resource_desc wined3d_desc; + + TRACE("iface %p, desc %p.\n", iface, desc); +@@ -708,7 +715,19 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3 + desc->SampleDesc.Quality = wined3d_desc.multisample_quality; + } + +-static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = ++static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, ++ gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ ++ TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ ++ wined3d_mutex_lock(); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ + d3d11_texture2d_QueryInterface, +@@ -725,13 +744,15 @@ static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetEvictionPriority, + /* ID3D11Texture2D methods */ + d3d11_texture2d_GetDesc, ++ /* IWineD3D11Texture methods */ ++ d3d11_texture2d_access_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) + { + if (!iface) + return NULL; +- assert(iface->lpVtbl == &d3d11_texture2d_vtbl); ++ assert(iface->lpVtbl == (void *)&d3d11_texture2d_vtbl); + return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); + } + +diff --git a/include/Makefile.in b/include/Makefile.in +index 9822bce6bdd..d9b646f7de1 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -770,6 +770,7 @@ SOURCES = \ + wine/winbase16.h \ + wine/windef16.h \ + wine/wine_common_ver.rc \ ++ wine/wined3d-interop.idl \ + wine/wined3d.h \ + wine/winedxgi.idl \ + wine/wingdi16.h \ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +new file mode 100644 +index 00000000000..884baf958c2 +--- /dev/null ++++ b/include/wine/wined3d-interop.idl +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2018 JĂ³zef Kucia 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 ++ */ ++ ++#if 0 ++#pragma makedep header install ++#endif ++ ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++ ++import "d3d11.idl"; ++ ++[ ++ object, ++ local, ++ uuid(267dc993-d15e-4015-aaac-b7559e226cc3) ++] ++interface IWineD3D11Texture2D : ID3D11Texture2D ++{ ++ void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From 17c027811a6641eea3298f5cb24c69c796dca328 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 04/12] d3d11: Add IWineD3D11Device interface. + +--- + dlls/d3d11/d3d11_private.h | 1 + + dlls/d3d11/device.c | 52 ++++++++++++++++++++++++++++++++ + include/wine/wined3d-interop.idl | 12 ++++++++ + 3 files changed, 65 insertions(+) + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 2b1809726ca..ec0bd79fdd5 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -564,6 +564,7 @@ struct d3d_device + ID3D10Multithread ID3D10Multithread_iface; + IWineDXGIDeviceParent IWineDXGIDeviceParent_iface; + IUnknown *outer_unk; ++ IWineD3D11Device IWineD3D11Device_iface; + LONG refcount; + + BOOL d3d11_only; +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 41967dd0a1c..887e7de00e9 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3119,6 +3119,53 @@ static ULONG STDMETHODCALLTYPE d3d11_device_Release(ID3D11Device2 *iface) + return IUnknown_Release(device->outer_unk); + } + ++/* IWineD3D11Device methods */ ++ ++static inline struct d3d_device *impl_from_IWineD3D11Device(IWineD3D11Device *iface) ++{ ++ return CONTAINING_RECORD(iface, struct d3d_device, IWineD3D11Device_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE wine_device_QueryInterface(IWineD3D11Device *iface, REFIID riid, void **out) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_QueryInterface(device->outer_unk, riid, out); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_AddRef(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_AddRef(device->outer_unk); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_Release(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_Release(device->outer_unk); ++} ++ ++static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device *iface, ++ user_cs_callback callback, const void *data, unsigned int data_size) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p, callback %p, data %p, data_size %u.\n", iface, callback, data, data_size); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_run_cs_callback(device->wined3d_device, callback, data, data_size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11DeviceVtbl wine_device_vtbl = ++{ ++ /* IUnknown methods */ ++ wine_device_QueryInterface, ++ wine_device_AddRef, ++ wine_device_Release, ++ /* IWineD3D11Device methods */ ++ wine_device_run_on_command_stream, ++}; ++ + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, + const D3D11_SUBRESOURCE_DATA *data, ID3D11Buffer **buffer) + { +@@ -4264,6 +4311,10 @@ static HRESULT STDMETHODCALLTYPE d3d_device_inner_QueryInterface(IUnknown *iface + { + *out = &device->IWineDXGIDeviceParent_iface; + } ++ else if (IsEqualGUID(riid, &IID_IWineD3D11Device)) ++ { ++ *out = &device->IWineD3D11Device_iface; ++ } + else + { + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); +@@ -6648,6 +6699,7 @@ void d3d_device_init(struct d3d_device *device, void *outer_unknown) + device->ID3D10Device1_iface.lpVtbl = &d3d10_device1_vtbl; + device->ID3D10Multithread_iface.lpVtbl = &d3d10_multithread_vtbl; + device->IWineDXGIDeviceParent_iface.lpVtbl = &d3d_dxgi_device_parent_vtbl; ++ device->IWineD3D11Device_iface.lpVtbl = &wine_device_vtbl; + device->device_parent.ops = &d3d_wined3d_device_parent_ops; + device->refcount = 1; + /* COM aggregation always takes place */ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 884baf958c2..d5c91623b3c 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -29,3 +29,15 @@ interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); + } ++ ++typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); ++ ++[ ++ object, ++ local, ++ uuid(8f02de7e-d55d-457b-9423-83456e49c58a) ++] ++interface IWineD3D11Device : IUnknown ++{ ++ void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From af8a3f1e0c505ecb0a2f6a899989431e85013f74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 19 Apr 2018 14:04:49 +0200 +Subject: [PATCH 05/12] wined3d: Implement wined3d_device_wait_idle(). + +--- + dlls/d3d11/device.c | 12 ++++++++++++ + dlls/wined3d/cs.c | 20 ++++++++++++++++++++ + dlls/wined3d/device.c | 7 +++++++ + dlls/wined3d/wined3d_private.h | 1 + + include/wine/wined3d-interop.idl | 2 ++ + include/wine/wined3d.h | 1 + + 6 files changed, 43 insertions(+) + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 887e7de00e9..1ff3f32f2c2 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3156,6 +3156,17 @@ static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device + wined3d_mutex_unlock(); + } + ++static void STDMETHODCALLTYPE wine_device_wait_idle(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_wait_idle(device->wined3d_device); ++ wined3d_mutex_unlock(); ++} ++ + static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + { + /* IUnknown methods */ +@@ -3164,6 +3175,7 @@ static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + wine_device_Release, + /* IWineD3D11Device methods */ + wine_device_run_on_command_stream, ++ wine_device_wait_idle, + }; + + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index cf55385c3c1..0eeabdb6640 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -476,6 +477,11 @@ struct wined3d_cs_user_callback + BYTE data[1]; + }; + ++struct wined3d_cs_wait_idle ++{ ++ enum wined3d_cs_op opcode; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2788,6 +2794,19 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} ++ ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_wait_idle *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_WAIT_IDLE; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 286d4d3acf5..a3e41cb38ef 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6328,3 +6328,10 @@ void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, + + wined3d_cs_emit_user_callback(device->cs, callback, data, size); + } ++ ++void CDECL wined3d_device_wait_idle(struct wined3d_device *device) ++{ ++ TRACE("device %p.\n", device); ++ ++ wined3d_cs_emit_wait_idle(device->cs); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 602647b1e94..8a38f6d4925 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index d5c91623b3c..6f8ea3770e3 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -40,4 +40,6 @@ typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_siz + interface IWineD3D11Device : IUnknown + { + void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++ ++ void wait_idle(); + } +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 39689bdb4e1..c90a71dbd5b 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2978,6 +2978,7 @@ typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size) + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, + wined3d_cs_callback callback, const void *data, unsigned int size); ++void __cdecl wined3d_device_wait_idle(struct wined3d_device *device); + + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) +-- +2.30.2 + +From 9c84c7ec7714dc3f0f9038b60735788f9d37d77c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 1 May 2018 15:06:30 -0500 +Subject: [PATCH 06/12] wined3d: Support retrieving depth texture in GL texture + callback + +--- + dlls/d3d11/texture.c | 20 +++++++++++++++++--- + dlls/wined3d/cs.c | 14 ++++++++++++-- + dlls/wined3d/texture.c | 7 ++++--- + dlls/wined3d/wined3d_private.h | 3 ++- + include/wine/wined3d-interop.idl | 4 ++-- + include/wine/wined3d.h | 4 ++-- + 6 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index dd21c6d7f08..c46aae99a24 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,14 +716,28 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, const void *data, unsigned int size) ++ gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ IWineD3D11Texture2D *depth_d3d11 = NULL; + + TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); + + wined3d_mutex_lock(); +- wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ ++ if (depth_unk) ++ { ++ HRESULT hr; ++ hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); ++ if(hr == S_OK) ++ depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); ++ } ++ ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ ++ if (depth_d3d11) ++ IWineD3D11Texture2D_Release(depth_d3d11); ++ + wined3d_mutex_unlock(); + } + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 0eeabdb6640..edfdeecee3c 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -464,6 +464,7 @@ struct wined3d_cs_gl_texture_callback + { + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; ++ struct wined3d_texture *depth_texture; + wined3d_gl_texture_callback callback; + unsigned int data_size; + BYTE data[1]; +@@ -2727,6 +2728,7 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + { + const struct wined3d_cs_gl_texture_callback *op = data; + struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ struct wined3d_texture_gl *depth_texture = wined3d_texture_gl(op->depth_texture); + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + +@@ -2734,8 +2736,12 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + gl_info = wined3d_context_gl(context)->gl_info; + + wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ if (depth_texture) ++ wined3d_texture_load_location(&depth_texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + +- op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ op->callback(texture->texture_rgb.name, ++ depth_texture ? depth_texture->texture_rgb.name : 0, ++ op->data, op->data_size); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +@@ -2750,13 +2750,15 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_cs_gl_texture_callback *op; + + op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; + op->texture = texture; ++ op->depth_texture = depth_texture; + op->callback = callback; + op->data_size = size; + memcpy(op->data, data, size); +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index bdcd0b55ae6..b8fa5d7d3ae 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4575,13 +4575,14 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + } + + void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_device *device = texture->resource.device; + +- TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ TRACE("texture %p, depth_texture %p, callback %p, data %p, size %u.\n", texture, depth_texture, callback, data, size); + +- wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 8a38f6d4925..a01ee1690c1 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4763,7 +4763,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 6f8ea3770e3..c5395ccc0ed 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -16,7 +16,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +-typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size); + + import "d3d11.idl"; + +@@ -27,7 +27,7 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index c90a71dbd5b..81235a7677c 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,10 +2969,10 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + +-typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int size); + + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + +-- +2.30.2 + +From d00f400da6cc085c207d0501944804ad63cb486b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 23:10:14 +0200 +Subject: [PATCH 07/12] wined3d: Get rid of wined3d_cs_emit_wait_idle(). + +--- + dlls/wined3d/cs.c | 15 --------------- + dlls/wined3d/device.c | 2 +- + dlls/wined3d/wined3d_private.h | 1 - + 3 files changed, 1 insertion(+), 17 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index edfdeecee3c..298f74c46c0 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -2804,19 +2803,6 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + +-static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} +- +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) +-{ +- struct wined3d_cs_wait_idle *op; +- +- op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); +- op->opcode = WINED3D_CS_OP_WAIT_IDLE; +- +- cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +- cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); +-} +- + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index a3e41cb38ef..9ec837e837d 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6333,5 +6333,5 @@ void CDECL wined3d_device_wait_idle(struct wined3d_device *device) + { + TRACE("device %p.\n", device); + +- wined3d_cs_emit_wait_idle(device->cs); ++ device->cs->c.ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index a01ee1690c1..0a18d909c5e 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, +-- +2.30.2 + +From 08d1dd39399a79f3f701cbcb37b733a5f48af5fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 22:04:21 +0200 +Subject: [PATCH 08/12] wined3d: Implement synchronous texture access. + +For vrclient. +--- + dlls/d3d11/texture.c | 15 ++++ + dlls/wined3d/cs.c | 48 ++++++++++++ + dlls/wined3d/device.c | 2 + + dlls/wined3d/texture.c | 123 +++++++++++++++++++++++++++++++ + dlls/wined3d/wined3d_private.h | 15 ++++ + include/wine/wined3d-interop.idl | 2 + + include/wine/wined3d.h | 2 + + 7 files changed, 207 insertions(+) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index c46aae99a24..7f7e8254225 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -741,6 +741,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Textur + wined3d_mutex_unlock(); + } + ++static unsigned int STDMETHODCALLTYPE d3d11_texture2d_get_gl_texture(IWineD3D11Texture2D *iface) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ unsigned int id; ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ id = wined3d_get_gl_texture(texture->wined3d_texture); ++ wined3d_mutex_unlock(); ++ ++ return id; ++} ++ + static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ +@@ -760,6 +774,7 @@ static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetDesc, + /* IWineD3D11Texture methods */ + d3d11_texture2d_access_gl_texture, ++ d3d11_texture2d_get_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 298f74c46c0..511883924a1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -79,6 +79,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_USER_CALLBACK, ++ WINED3D_CS_OP_FENCE, + WINED3D_CS_OP_STOP, + }; + +@@ -482,6 +483,12 @@ struct wined3d_cs_wait_idle + enum wined3d_cs_op opcode; + }; + ++struct wined3d_cs_fence ++{ ++ enum wined3d_cs_op opcode; ++ GLsync *fence; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2803,6 +2810,46 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_fence *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ GLsync fence; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); ++ wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); ++ ++ *op->fence = fence; ++ ++ checkGLcall("fence"); ++ ++ context_release(context); ++} ++ ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_fence *op; ++ GLsync fence; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_FENCE; ++ op->fence = &fence; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++ ++ return fence; ++} ++ ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++{ ++ return wined3d_cs_emit_fence(cs); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2918,6 +2965,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, ++ /* WINED3D_CS_OP_FENCE */ wined3d_cs_exec_fence, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 9ec837e837d..9d042f4d0e1 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -218,6 +218,8 @@ void wined3d_device_cleanup(struct wined3d_device *device) + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + ++ wined3d_destroy_gl_vr_context(&device->vr_context); ++ + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index b8fa5d7d3ae..25c5e57fca0 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4585,6 +4585,129 @@ void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + ++static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) ++{ ++ const struct wined3d_adapter *adapter = device->adapter; ++ const struct wined3d_gl_info *gl_info = &wined3d_adapter_gl_const(adapter)->gl_info; ++ struct wined3d_vr_gl_context *ctx = &device->vr_context; ++ PIXELFORMATDESCRIPTOR pfd; ++ int pixel_format; ++ HGLRC share_ctx; ++ ++ if (ctx->gl_info) ++ return gl_info; ++ ++ TRACE("Creating GL context.\n"); ++ ++ if (!gl_info->p_wglCreateContextAttribsARB) ++ { ++ ERR("wglCreateContextAttribsARB is not supported.\n"); ++ return NULL; ++ } ++ ++ if (!gl_info->supported[ARB_SYNC]) ++ { ++ FIXME("ARB_sync is not supported.\n"); ++ return NULL; ++ } ++ ++ ctx->window = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D VR window", ++ WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); ++ if (!ctx->window) ++ { ++ ERR("Failed to create a window.\n"); ++ return NULL; ++ } ++ ++ ctx->dc = GetDC(ctx->window); ++ if (!ctx->dc) ++ { ++ ERR("Failed to get a DC.\n"); ++ goto fail; ++ } ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd.nSize = sizeof(pfd); ++ pfd.nVersion = 1; ++ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; ++ pfd.iPixelType = PFD_TYPE_RGBA; ++ pfd.cColorBits = 32; ++ pfd.iLayerType = PFD_MAIN_PLANE; ++ ++ if (!(pixel_format = ChoosePixelFormat(ctx->dc, &pfd))) ++ { ++ ERR("Failed to find a suitable pixel format.\n"); ++ goto fail; ++ } ++ DescribePixelFormat(ctx->dc, pixel_format, sizeof(pfd), &pfd); ++ SetPixelFormat(ctx->dc, pixel_format, &pfd); ++ ++ share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; ++ if (!(ctx->gl_ctx = context_create_wgl_attribs(gl_info, ctx->dc, share_ctx))) ++ { ++ WARN("Failed to create GL context for VR.\n"); ++ goto fail; ++ } ++ ++ if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) ++ { ++ ERR("Failed to make GL context current.\n"); ++ goto fail; ++ } ++ ++ checkGLcall("create context"); ++ ++ ctx->gl_info = gl_info; ++ return gl_info; ++ ++fail: ++ if (ctx->gl_ctx) ++ wglDeleteContext(ctx->gl_ctx); ++ ctx->gl_ctx = NULL; ++ if (ctx->dc) ++ ReleaseDC(ctx->window, ctx->dc); ++ ctx->dc = NULL; ++ if (ctx->window) ++ DestroyWindow(ctx->window); ++ ctx->window = NULL; ++ return NULL; ++} ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) ++{ ++ if (!ctx->gl_info) ++ return; ++ ++ TRACE("Destroying GL context.\n"); ++ ++ wglMakeCurrent(NULL, NULL); ++ wglDeleteContext(ctx->gl_ctx); ++ ReleaseDC(ctx->window, ctx->dc); ++ DestroyWindow(ctx->window); ++} ++ ++unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_texture_gl *gl_texture; ++ GLsync fence; ++ ++ TRACE("texture %p.\n", texture); ++ ++ if (!(gl_info = wined3d_prepare_vr_gl_context(device))) ++ return 0; ++ ++ fence = wined3d_cs_synchronize(device->cs); ++ GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); ++ GL_EXTCALL(glDeleteSync(fence)); ++ ++ checkGLcall("synchronize CS"); ++ ++ gl_texture = wined3d_texture_gl(texture); ++ return gl_texture->texture_rgb.name; ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 0a18d909c5e..4bd32d95cda 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -50,6 +50,7 @@ + #include "wine/wined3d.h" + #include "wine/list.h" + #include "wine/rbtree.h" ++#include "wine/wgl.h" + + static inline size_t align(size_t addr, size_t alignment) + { +@@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry + struct wined3d_stream_output_element elements[1]; + }; + ++struct wined3d_vr_gl_context ++{ ++ HWND window; ++ HDC dc; ++ HGLRC gl_ctx; ++ const struct wined3d_gl_info *gl_info; ++}; ++ + struct wined3d_device + { + LONG ref; +@@ -3842,6 +3850,8 @@ struct wined3d_device + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; ++ ++ struct wined3d_vr_gl_context vr_context; + + CRITICAL_SECTION bo_map_lock; + }; +@@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++ ++ + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index c5395ccc0ed..b1bda8ada76 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -28,6 +28,8 @@ import "d3d11.idl"; + interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ ++ unsigned int get_gl_texture(); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 81235a7677c..d63c54ca593 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,8 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, uns + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + ++unsigned int __cdecl wined3d_get_gl_texture(struct wined3d_texture *texture); ++ + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, +-- +2.30.2 + +From cc1f77d00ee61e8620029c08110015543682acb8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:04:07 +0200 +Subject: [PATCH 09/12] wined3d: Load TEXTURE_RGB location for synchronous + texture access. + +--- + dlls/wined3d/cs.c | 10 +++++++--- + dlls/wined3d/texture.c | 2 +- + dlls/wined3d/wined3d_private.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 511883924a1..6242651dd72 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -486,6 +486,7 @@ struct wined3d_cs_wait_idle + struct wined3d_cs_fence + { + enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; + GLsync *fence; + }; + +@@ -2820,6 +2821,8 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context = context_acquire(cs->c.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + ++ wined3d_texture_load_location(op->texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ + fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); + +@@ -2830,13 +2833,14 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context_release(context); + } + +-static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs, struct wined3d_texture *texture) + { + struct wined3d_cs_fence *op; + GLsync fence; + + op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FENCE; ++ op->texture = texture; + op->fence = &fence; + + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +@@ -2845,9 +2849,9 @@ static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) + return fence; + } + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) + { +- return wined3d_cs_emit_fence(cs); ++ return wined3d_cs_emit_fence(cs, texture); + } + + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 25c5e57fca0..72a64889ca9 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4698,7 +4698,7 @@ unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) + if (!(gl_info = wined3d_prepare_vr_gl_context(device))) + return 0; + +- fence = wined3d_cs_synchronize(device->cs); ++ fence = wined3d_cs_synchronize(device->cs, texture); + GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); + GL_EXTCALL(glDeleteSync(fence)); + +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 4bd32d95cda..2a9b2c08877 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; + + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + +-- +2.30.2 + +From 29f4110ef25083ad84d3f441986345abf79f3eaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:17:15 +0200 +Subject: [PATCH 10/12] d3d11: Pass IWineD3D11Texture2D to access_gl_texture(). + +--- + dlls/d3d11/texture.c | 21 +++++++-------------- + include/wine/wined3d-interop.idl | 3 ++- + 2 files changed, 9 insertions(+), 15 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 7f7e8254225..32bf3c3c9cc 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,27 +716,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) ++ gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { + struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; +- IWineD3D11Texture2D *depth_d3d11 = NULL; ++ struct wined3d_texture *wined3d_depth_texture = NULL; + +- TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", ++ iface, callback, depth_texture, data, size); + + wined3d_mutex_lock(); + +- if (depth_unk) +- { +- HRESULT hr; +- hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); +- if(hr == S_OK) +- depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); +- } +- +- wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ if (depth_texture) ++ wined3d_depth_texture = impl_from_IWineD3D11Texture2D(depth_texture)->wined3d_texture; + +- if (depth_d3d11) +- IWineD3D11Texture2D_Release(depth_d3d11); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, wined3d_depth_texture, data, size); + + wined3d_mutex_unlock(); + } +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index b1bda8ada76..f960e2f6d9d 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -27,7 +27,8 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, ++ IWineD3D11Texture2D *depth_texture, const void *data, unsigned int data_size); + + unsigned int get_gl_texture(); + } +-- +2.30.2 + +From 9a97a24ad5001cc4d25b9b8236fbf13b76d9eaef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 11 Oct 2018 11:31:43 +0200 +Subject: [PATCH 11/12] d3d11: Remove unused 'depth_tex' variable. + +--- + dlls/d3d11/texture.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 32bf3c3c9cc..f6e7d474d99 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -718,7 +718,7 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, + gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_texture *wined3d_depth_texture = NULL; + + TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", +-- +2.30.2 + +From b476c31f53be82ad3b8a3ec9d7987f75e8040cfe Mon Sep 17 00:00:00 2001 +From: GloriousEggroll +Date: Sun, 18 Aug 2019 19:05:52 -0600 +Subject: [PATCH 12/12] wined3d + +--- + dlls/wined3d/wined3d.spec | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec +index 2879e1653d3..bbc9ce7b99c 100644 +--- a/dlls/wined3d/wined3d.spec ++++ b/dlls/wined3d/wined3d.spec +@@ -367,6 +367,11 @@ + @ cdecl wined3d_vertex_declaration_decref(ptr) + @ cdecl wined3d_vertex_declaration_get_parent(ptr) + @ cdecl wined3d_vertex_declaration_incref(ptr) ++ ++@ cdecl wined3d_access_gl_texture(ptr ptr ptr long) ++@ cdecl wined3d_device_run_cs_callback(ptr ptr ptr long) ++@ cdecl wined3d_device_wait_idle(ptr) ++@ cdecl wined3d_get_gl_texture(ptr) + + @ cdecl vkd3d_create_instance(ptr ptr) + @ cdecl vkd3d_instance_decref(ptr) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-c065b4f.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-c065b4f.patch new file mode 100644 index 000000000..cdc91342e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/legacy/proton-vr-c065b4f.patch @@ -0,0 +1,1622 @@ +From 4ad17bc8a5651c65b7e1bced3c9e04bce9f77c91 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 01/12] wined3d: Implement GL texture access callbacks. + +--- + dlls/wined3d/cs.c | 52 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/texture.c | 10 +++++++ + dlls/wined3d/wined3d_private.h | 3 ++ + include/wine/wined3d.h | 5 ++++ + 4 files changed, 70 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 21aaa2e54b6..e291e2b550d 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -77,6 +77,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_COPY_UAV_COUNTER, + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, ++ WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -457,6 +458,15 @@ struct wined3d_cs_generate_mipmaps + struct wined3d_shader_resource_view *view; + }; + ++struct wined3d_cs_gl_texture_callback ++{ ++ enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; ++ wined3d_gl_texture_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2698,6 +2708,44 @@ void wined3d_device_context_emit_generate_mipmaps(struct wined3d_device_context + wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_gl_texture_callback *op = data; ++ struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ ++ op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ ++ context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); ++ context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); ++ ++ checkGLcall("texture callback\n"); ++ ++ context_release(context); ++ ++} ++ ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_gl_texture_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; ++ op->texture = texture; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2865,6 +2916,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_COPY_UAV_COUNTER */ wined3d_cs_exec_copy_uav_counter, + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, ++ /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 39e6e2a68b7..bdcd0b55ae6 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4574,6 +4574,16 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location); + } + ++void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ ++ TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index f5b1a078907..756d47ef20d 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4762,6 +4762,9 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + cs->c.ops->finish(&cs->c, queue_id); + } + ++void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index dd1c15f14ed..2123e857a52 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,6 +2969,11 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++ ++void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 34565d56b8cfcdcf787c5a3a0466f990918d1139 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 02/12] wined3d: Implement command stream callbacks. + +--- + dlls/wined3d/cs.c | 40 ++++++++++++++++++++++++++++++++++ + dlls/wined3d/device.c | 8 +++++++ + dlls/wined3d/wined3d_private.h | 2 ++ + include/wine/wined3d.h | 5 +++++ + 4 files changed, 55 insertions(+) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index e291e2b550d..cf55385c3c1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -78,6 +78,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_GENERATE_MIPMAPS, + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, ++ WINED3D_CS_OP_USER_CALLBACK, + WINED3D_CS_OP_STOP, + }; + +@@ -467,6 +468,14 @@ struct wined3d_cs_gl_texture_callback + BYTE data[1]; + }; + ++struct wined3d_cs_user_callback ++{ ++ enum wined3d_cs_op opcode; ++ wined3d_cs_callback callback; ++ unsigned int data_size; ++ BYTE data[1]; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2749,6 +2758,36 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_user_callback(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_user_callback *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ op->callback(op->data, op->data_size); ++ ++ checkGLcall("user callback\n"); ++ ++ context_release(context); ++} ++ ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ struct wined3d_cs_user_callback *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_USER_CALLBACK; ++ op->callback = callback; ++ op->data_size = size; ++ memcpy(op->data, data, size); ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2893,6 +2932,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_GENERATE_MIPMAPS */ wined3d_cs_exec_generate_mipmaps, + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, ++ /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 484130b3103..286d4d3acf5 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6320,3 +6320,11 @@ LRESULT device_process_message(struct wined3d_device *device, HWND window, BOOL + else + return CallWindowProcA(proc, window, message, wparam, lparam); + } ++ ++void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size) ++{ ++ TRACE("device %p, callback %p, data %p, size %u.\n", device, callback, data, size); ++ ++ wined3d_cs_emit_user_callback(device->cs, callback, data, size); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 756d47ef20d..602647b1e94 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, ++ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 2123e857a52..39689bdb4e1 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,11 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, con + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, const void *data, unsigned int size); + ++typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); ++ ++void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, ++ wined3d_cs_callback callback, const void *data, unsigned int size); ++ + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) + { +-- +2.30.2 + +From 434e12794d7dda50554dbcf781af282816d39121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 03/12] d3d11: Add IWineD3D11Texture2D interface. + +--- + dlls/d3d11/d3d11_private.h | 4 +- + dlls/d3d11/device.c | 4 +- + dlls/d3d11/texture.c | 65 +++++++++++++++++++++----------- + include/Makefile.in | 1 + + include/wine/wined3d-interop.idl | 31 +++++++++++++++ + 5 files changed, 80 insertions(+), 25 deletions(-) + create mode 100644 include/wine/wined3d-interop.idl + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 1a8cdc6d77c..2b1809726ca 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -39,6 +39,8 @@ + #include "wine/winedxgi.h" + #include "wine/rbtree.h" + ++#include "wine/wined3d-interop.h" ++ + struct d3d_device; + + /* TRACE helper functions */ +@@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D + /* ID3D11Texture2D, ID3D10Texture2D */ + struct d3d_texture2d + { +- ID3D11Texture2D ID3D11Texture2D_iface; ++ IWineD3D11Texture2D ID3D11Texture2D_iface; + ID3D10Texture2D ID3D10Texture2D_iface; + LONG refcount; + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 0819918b2a1..41967dd0a1c 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3165,7 +3165,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_device_CreateTexture2D(ID3D11Device2 *ifa + if (FAILED(hr = d3d_texture2d_create(device, desc, data, &object))) + return hr; + +- *texture = &object->ID3D11Texture2D_iface; ++ *texture = (ID3D11Texture2D *)&object->ID3D11Texture2D_iface; + + return S_OK; + } +@@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic + return hr; + + hr = IUnknown_QueryInterface(object->dxgi_resource, &IID_IDXGISurface, (void **)ret_surface); +- ID3D11Texture2D_Release(&object->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&object->ID3D11Texture2D_iface); + return hr; + } + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 061bbb09795..dd21c6d7f08 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -518,14 +518,21 @@ HRESULT d3d_texture1d_create(struct d3d_device *device, const D3D11_TEXTURE1D_DE + + /* ID3D11Texture2D methods */ + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D *iface, REFIID riid, void **object) ++static inline struct d3d_texture2d *impl_from_IWineD3D11Texture2D(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); +- HRESULT hr; ++ return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(IWineD3D11Texture2D *iface, ++ REFIID riid, void **object) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ HRESULT hr; + + TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); + +- if (IsEqualGUID(riid, &IID_ID3D11Texture2D) ++ if (IsEqualGUID(riid, &IID_IWineD3D11Texture2D) ++ || IsEqualGUID(riid, &IID_ID3D11Texture2D) + || IsEqualGUID(riid, &IID_ID3D11Resource) + || IsEqualGUID(riid, &IID_ID3D11DeviceChild) + || IsEqualGUID(riid, &IID_IUnknown)) +@@ -554,9 +561,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_QueryInterface(ID3D11Texture2D + return E_NOINTERFACE; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedIncrement(&texture->refcount); + + TRACE("%p increasing refcount to %u.\n", texture, refcount); +@@ -572,9 +579,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_AddRef(ID3D11Texture2D *iface) + return refcount; + } + +-static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) ++static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(IWineD3D11Texture2D *iface) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + ULONG refcount = InterlockedDecrement(&texture->refcount); + + TRACE("%p decreasing refcount to %u.\n", texture, refcount); +@@ -594,9 +601,9 @@ static ULONG STDMETHODCALLTYPE d3d11_texture2d_Release(ID3D11Texture2D *iface) + return refcount; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, ID3D11Device **device) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(IWineD3D11Texture2D *iface, ID3D11Device **device) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + + TRACE("iface %p, device %p.\n", iface, device); + +@@ -604,10 +611,10 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDevice(ID3D11Texture2D *iface, + ID3D11Device_AddRef(*device); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT *data_size, void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -624,10 +631,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_GetPrivateData(ID3D11Texture2D + return d3d_get_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(IWineD3D11Texture2D *iface, + REFGUID guid, UINT data_size, const void *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -644,10 +651,10 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateData(ID3D11Texture2D + return d3d_set_private_data(&texture->private_store, guid, data_size, data); + } + +-static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11Texture2D *iface, ++static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(IWineD3D11Texture2D *iface, + REFGUID guid, const IUnknown *data) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + IDXGIResource *dxgi_resource; + HRESULT hr; + +@@ -664,7 +671,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_texture2d_SetPrivateDataInterface(ID3D11T + return d3d_set_private_data_interface(&texture->private_store, guid, data); + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, ++static void STDMETHODCALLTYPE d3d11_texture2d_GetType(IWineD3D11Texture2D *iface, + D3D11_RESOURCE_DIMENSION *resource_dimension) + { + TRACE("iface %p, resource_dimension %p.\n", iface, resource_dimension); +@@ -672,21 +679,21 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetType(ID3D11Texture2D *iface, + *resource_dimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(ID3D11Texture2D *iface, UINT eviction_priority) ++static void STDMETHODCALLTYPE d3d11_texture2d_SetEvictionPriority(IWineD3D11Texture2D *iface, UINT eviction_priority) + { + FIXME("iface %p, eviction_priority %#x stub!\n", iface, eviction_priority); + } + +-static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(ID3D11Texture2D *iface) ++static UINT STDMETHODCALLTYPE d3d11_texture2d_GetEvictionPriority(IWineD3D11Texture2D *iface) + { + FIXME("iface %p stub!\n", iface); + + return 0; + } + +-static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) ++static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface, D3D11_TEXTURE2D_DESC *desc) + { +- struct d3d_texture2d *texture = impl_from_ID3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_resource_desc wined3d_desc; + + TRACE("iface %p, desc %p.\n", iface, desc); +@@ -708,7 +715,19 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(ID3D11Texture2D *iface, D3 + desc->SampleDesc.Quality = wined3d_desc.multisample_quality; + } + +-static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = ++static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, ++ gl_texture_callback callback, const void *data, unsigned int size) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ ++ TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ ++ wined3d_mutex_lock(); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ + d3d11_texture2d_QueryInterface, +@@ -725,13 +744,15 @@ static const struct ID3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetEvictionPriority, + /* ID3D11Texture2D methods */ + d3d11_texture2d_GetDesc, ++ /* IWineD3D11Texture methods */ ++ d3d11_texture2d_access_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) + { + if (!iface) + return NULL; +- assert(iface->lpVtbl == &d3d11_texture2d_vtbl); ++ assert(iface->lpVtbl == (void *)&d3d11_texture2d_vtbl); + return CONTAINING_RECORD(iface, struct d3d_texture2d, ID3D11Texture2D_iface); + } + +diff --git a/include/Makefile.in b/include/Makefile.in +index 9822bce6bdd..d9b646f7de1 100644 +--- a/include/Makefile.in ++++ b/include/Makefile.in +@@ -770,6 +770,7 @@ SOURCES = \ + wine/winbase16.h \ + wine/windef16.h \ + wine/wine_common_ver.rc \ ++ wine/wined3d-interop.idl \ + wine/wined3d.h \ + wine/winedxgi.idl \ + wine/wingdi16.h \ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +new file mode 100644 +index 00000000000..884baf958c2 +--- /dev/null ++++ b/include/wine/wined3d-interop.idl +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2018 JĂ³zef Kucia 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 ++ */ ++ ++#if 0 ++#pragma makedep header install ++#endif ++ ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++ ++import "d3d11.idl"; ++ ++[ ++ object, ++ local, ++ uuid(267dc993-d15e-4015-aaac-b7559e226cc3) ++] ++interface IWineD3D11Texture2D : ID3D11Texture2D ++{ ++ void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From 17c027811a6641eea3298f5cb24c69c796dca328 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Wed, 4 Apr 2018 17:05:37 +0200 +Subject: [PATCH 04/12] d3d11: Add IWineD3D11Device interface. + +--- + dlls/d3d11/d3d11_private.h | 1 + + dlls/d3d11/device.c | 52 ++++++++++++++++++++++++++++++++ + include/wine/wined3d-interop.idl | 12 ++++++++ + 3 files changed, 65 insertions(+) + +diff --git a/dlls/d3d11/d3d11_private.h b/dlls/d3d11/d3d11_private.h +index 2b1809726ca..ec0bd79fdd5 100644 +--- a/dlls/d3d11/d3d11_private.h ++++ b/dlls/d3d11/d3d11_private.h +@@ -564,6 +564,7 @@ struct d3d_device + ID3D10Multithread ID3D10Multithread_iface; + IWineDXGIDeviceParent IWineDXGIDeviceParent_iface; + IUnknown *outer_unk; ++ IWineD3D11Device IWineD3D11Device_iface; + LONG refcount; + + BOOL d3d11_only; +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 41967dd0a1c..887e7de00e9 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3119,6 +3119,53 @@ static ULONG STDMETHODCALLTYPE d3d11_device_Release(ID3D11Device2 *iface) + return IUnknown_Release(device->outer_unk); + } + ++/* IWineD3D11Device methods */ ++ ++static inline struct d3d_device *impl_from_IWineD3D11Device(IWineD3D11Device *iface) ++{ ++ return CONTAINING_RECORD(iface, struct d3d_device, IWineD3D11Device_iface); ++} ++ ++static HRESULT STDMETHODCALLTYPE wine_device_QueryInterface(IWineD3D11Device *iface, REFIID riid, void **out) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_QueryInterface(device->outer_unk, riid, out); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_AddRef(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_AddRef(device->outer_unk); ++} ++ ++static ULONG STDMETHODCALLTYPE wine_device_Release(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ return IUnknown_Release(device->outer_unk); ++} ++ ++static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device *iface, ++ user_cs_callback callback, const void *data, unsigned int data_size) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p, callback %p, data %p, data_size %u.\n", iface, callback, data, data_size); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_run_cs_callback(device->wined3d_device, callback, data, data_size); ++ wined3d_mutex_unlock(); ++} ++ ++static const struct IWineD3D11DeviceVtbl wine_device_vtbl = ++{ ++ /* IUnknown methods */ ++ wine_device_QueryInterface, ++ wine_device_AddRef, ++ wine_device_Release, ++ /* IWineD3D11Device methods */ ++ wine_device_run_on_command_stream, ++}; ++ + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, + const D3D11_SUBRESOURCE_DATA *data, ID3D11Buffer **buffer) + { +@@ -4264,6 +4311,10 @@ static HRESULT STDMETHODCALLTYPE d3d_device_inner_QueryInterface(IUnknown *iface + { + *out = &device->IWineDXGIDeviceParent_iface; + } ++ else if (IsEqualGUID(riid, &IID_IWineD3D11Device)) ++ { ++ *out = &device->IWineD3D11Device_iface; ++ } + else + { + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); +@@ -6648,6 +6699,7 @@ void d3d_device_init(struct d3d_device *device, void *outer_unknown) + device->ID3D10Device1_iface.lpVtbl = &d3d10_device1_vtbl; + device->ID3D10Multithread_iface.lpVtbl = &d3d10_multithread_vtbl; + device->IWineDXGIDeviceParent_iface.lpVtbl = &d3d_dxgi_device_parent_vtbl; ++ device->IWineD3D11Device_iface.lpVtbl = &wine_device_vtbl; + device->device_parent.ops = &d3d_wined3d_device_parent_ops; + device->refcount = 1; + /* COM aggregation always takes place */ +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 884baf958c2..d5c91623b3c 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -29,3 +29,15 @@ interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); + } ++ ++typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); ++ ++[ ++ object, ++ local, ++ uuid(8f02de7e-d55d-457b-9423-83456e49c58a) ++] ++interface IWineD3D11Device : IUnknown ++{ ++ void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++} +-- +2.30.2 + +From af8a3f1e0c505ecb0a2f6a899989431e85013f74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 19 Apr 2018 14:04:49 +0200 +Subject: [PATCH 05/12] wined3d: Implement wined3d_device_wait_idle(). + +--- + dlls/d3d11/device.c | 12 ++++++++++++ + dlls/wined3d/cs.c | 20 ++++++++++++++++++++ + dlls/wined3d/device.c | 7 +++++++ + dlls/wined3d/wined3d_private.h | 1 + + include/wine/wined3d-interop.idl | 2 ++ + include/wine/wined3d.h | 1 + + 6 files changed, 43 insertions(+) + +diff --git a/dlls/d3d11/device.c b/dlls/d3d11/device.c +index 887e7de00e9..1ff3f32f2c2 100644 +--- a/dlls/d3d11/device.c ++++ b/dlls/d3d11/device.c +@@ -3156,6 +3156,17 @@ static void STDMETHODCALLTYPE wine_device_run_on_command_stream(IWineD3D11Device + wined3d_mutex_unlock(); + } + ++static void STDMETHODCALLTYPE wine_device_wait_idle(IWineD3D11Device *iface) ++{ ++ struct d3d_device *device = impl_from_IWineD3D11Device(iface); ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ wined3d_device_wait_idle(device->wined3d_device); ++ wined3d_mutex_unlock(); ++} ++ + static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + { + /* IUnknown methods */ +@@ -3164,6 +3175,7 @@ static const struct IWineD3D11DeviceVtbl wine_device_vtbl = + wine_device_Release, + /* IWineD3D11Device methods */ + wine_device_run_on_command_stream, ++ wine_device_wait_idle, + }; + + static HRESULT STDMETHODCALLTYPE d3d11_device_CreateBuffer(ID3D11Device2 *iface, const D3D11_BUFFER_DESC *desc, +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index cf55385c3c1..0eeabdb6640 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -476,6 +477,11 @@ struct wined3d_cs_user_callback + BYTE data[1]; + }; + ++struct wined3d_cs_wait_idle ++{ ++ enum wined3d_cs_op opcode; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2788,6 +2794,19 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} ++ ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_wait_idle *op; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_WAIT_IDLE; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 286d4d3acf5..a3e41cb38ef 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6328,3 +6328,10 @@ void CDECL wined3d_device_run_cs_callback(struct wined3d_device *device, + + wined3d_cs_emit_user_callback(device->cs, callback, data, size); + } ++ ++void CDECL wined3d_device_wait_idle(struct wined3d_device *device) ++{ ++ TRACE("device %p.\n", device); ++ ++ wined3d_cs_emit_wait_idle(device->cs); ++} +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 602647b1e94..8a38f6d4925 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index d5c91623b3c..6f8ea3770e3 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -40,4 +40,6 @@ typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_siz + interface IWineD3D11Device : IUnknown + { + void run_on_command_stream(user_cs_callback callback, const void *data, unsigned int data_size); ++ ++ void wait_idle(); + } +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 39689bdb4e1..c90a71dbd5b 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2978,6 +2978,7 @@ typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size) + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, + wined3d_cs_callback callback, const void *data, unsigned int size); ++void __cdecl wined3d_device_wait_idle(struct wined3d_device *device); + + /* Return the integer base-2 logarithm of x. Undefined for x == 0. */ + static inline unsigned int wined3d_log2i(unsigned int x) +-- +2.30.2 + +From 9c84c7ec7714dc3f0f9038b60735788f9d37d77c Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 1 May 2018 15:06:30 -0500 +Subject: [PATCH 06/12] wined3d: Support retrieving depth texture in GL texture + callback + +--- + dlls/d3d11/texture.c | 20 +++++++++++++++++--- + dlls/wined3d/cs.c | 14 ++++++++++++-- + dlls/wined3d/texture.c | 7 ++++--- + dlls/wined3d/wined3d_private.h | 3 ++- + include/wine/wined3d-interop.idl | 4 ++-- + include/wine/wined3d.h | 4 ++-- + 6 files changed, 39 insertions(+), 13 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index dd21c6d7f08..c46aae99a24 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,14 +716,28 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, const void *data, unsigned int size) ++ gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ IWineD3D11Texture2D *depth_d3d11 = NULL; + + TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); + + wined3d_mutex_lock(); +- wined3d_access_gl_texture(texture->wined3d_texture, callback, data, size); ++ ++ if (depth_unk) ++ { ++ HRESULT hr; ++ hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); ++ if(hr == S_OK) ++ depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); ++ } ++ ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ ++ if (depth_d3d11) ++ IWineD3D11Texture2D_Release(depth_d3d11); ++ + wined3d_mutex_unlock(); + } + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 0eeabdb6640..edfdeecee3c 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -464,6 +464,7 @@ struct wined3d_cs_gl_texture_callback + { + enum wined3d_cs_op opcode; + struct wined3d_texture *texture; ++ struct wined3d_texture *depth_texture; + wined3d_gl_texture_callback callback; + unsigned int data_size; + BYTE data[1]; +@@ -2727,6 +2728,7 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + { + const struct wined3d_cs_gl_texture_callback *op = data; + struct wined3d_texture_gl *texture = wined3d_texture_gl(op->texture); ++ struct wined3d_texture_gl *depth_texture = wined3d_texture_gl(op->depth_texture); + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + +@@ -2734,8 +2736,12 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + gl_info = wined3d_context_gl(context)->gl_info; + + wined3d_texture_load_location(&texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ if (depth_texture) ++ wined3d_texture_load_location(&depth_texture->t, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + +- op->callback(texture->texture_rgb.name, op->data, op->data_size); ++ op->callback(texture->texture_rgb.name, ++ depth_texture ? depth_texture->texture_rgb.name : 0, ++ op->data, op->data_size); + + context_invalidate_compute_state(context, STATE_COMPUTE_SHADER_RESOURCE_BINDING); + context_invalidate_state(context, STATE_GRAPHICS_SHADER_RESOURCE_BINDING); +@@ -2750,13 +2750,15 @@ static void wined3d_cs_exec_gl_texture_callback(struct wined3d_cs *cs, const voi + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_cs_gl_texture_callback *op; + + op = cs->c.ops->require_space(cs, sizeof(*op) + size, WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_GL_TEXTURE_CALLBACK; + op->texture = texture; ++ op->depth_texture = depth_texture; + op->callback = callback; + op->data_size = size; + memcpy(op->data, data, size); +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index bdcd0b55ae6..b8fa5d7d3ae 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4575,13 +4575,14 @@ void wined3d_texture_download_from_texture(struct wined3d_texture *dst_texture, + } + + void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) + { + struct wined3d_device *device = texture->resource.device; + +- TRACE("texture %p, callback %p, data %p, size %u.\n", texture, callback, data, size); ++ TRACE("texture %p, depth_texture %p, callback %p, data %p, size %u.\n", texture, depth_texture, callback, data, size); + +- wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, data, size); ++ wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 8a38f6d4925..a01ee1690c1 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4763,7 +4763,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu + } + + void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, ++ const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index 6f8ea3770e3..c5395ccc0ed 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -16,7 +16,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +-typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int data_size); ++typedef void (__cdecl *gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size); + + import "d3d11.idl"; + +@@ -27,7 +27,7 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index c90a71dbd5b..81235a7677c 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2969,10 +2969,10 @@ ULONG __cdecl wined3d_vertex_declaration_incref(struct wined3d_vertex_declaratio + HRESULT __cdecl wined3d_extract_shader_input_signature_from_dxbc(struct wined3d_shader_signature *signature, + const void *byte_code, SIZE_T byte_code_size); + +-typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, const void *data, unsigned int size); ++typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int size); + + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); ++ wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + +-- +2.30.2 + +From d00f400da6cc085c207d0501944804ad63cb486b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 23:10:14 +0200 +Subject: [PATCH 07/12] wined3d: Get rid of wined3d_cs_emit_wait_idle(). + +--- + dlls/wined3d/cs.c | 15 --------------- + dlls/wined3d/device.c | 2 +- + dlls/wined3d/wined3d_private.h | 1 - + 3 files changed, 1 insertion(+), 17 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index edfdeecee3c..298f74c46c0 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -2804,19 +2803,6 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + +-static void wined3d_cs_exec_wait_idle(struct wined3d_cs *cs, const void *data) {} +- +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) +-{ +- struct wined3d_cs_wait_idle *op; +- +- op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); +- op->opcode = WINED3D_CS_OP_WAIT_IDLE; +- +- cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +- cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); +-} +- + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index a3e41cb38ef..9ec837e837d 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -6333,5 +6333,5 @@ void CDECL wined3d_device_wait_idle(struct wined3d_device *device) + { + TRACE("device %p.\n", device); + +- wined3d_cs_emit_wait_idle(device->cs); ++ device->cs->c.ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + } +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index a01ee1690c1..0a18d909c5e 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + const void *data, unsigned int size) DECLSPEC_HIDDEN; + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) +-- +2.30.2 + +From 08d1dd39399a79f3f701cbcb37b733a5f48af5fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Tue, 18 Sep 2018 22:04:21 +0200 +Subject: [PATCH 08/12] wined3d: Implement synchronous texture access. + +For vrclient. +--- + dlls/d3d11/texture.c | 15 ++++ + dlls/wined3d/cs.c | 48 ++++++++++++ + dlls/wined3d/device.c | 2 + + dlls/wined3d/texture.c | 123 +++++++++++++++++++++++++++++++ + dlls/wined3d/wined3d_private.h | 15 ++++ + include/wine/wined3d-interop.idl | 2 + + include/wine/wined3d.h | 2 + + 7 files changed, 207 insertions(+) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index c46aae99a24..7f7e8254225 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -741,6 +741,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Textur + wined3d_mutex_unlock(); + } + ++static unsigned int STDMETHODCALLTYPE d3d11_texture2d_get_gl_texture(IWineD3D11Texture2D *iface) ++{ ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); ++ unsigned int id; ++ ++ TRACE("iface %p.\n", iface); ++ ++ wined3d_mutex_lock(); ++ id = wined3d_get_gl_texture(texture->wined3d_texture); ++ wined3d_mutex_unlock(); ++ ++ return id; ++} ++ + static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + { + /* IUnknown methods */ +@@ -760,6 +774,7 @@ static const struct IWineD3D11Texture2DVtbl d3d11_texture2d_vtbl = + d3d11_texture2d_GetDesc, + /* IWineD3D11Texture methods */ + d3d11_texture2d_access_gl_texture, ++ d3d11_texture2d_get_gl_texture, + }; + + struct d3d_texture2d *unsafe_impl_from_ID3D11Texture2D(ID3D11Texture2D *iface) +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 298f74c46c0..511883924a1 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -79,6 +79,7 @@ enum wined3d_cs_op + WINED3D_CS_OP_EXECUTE_COMMAND_LIST, + WINED3D_CS_OP_GL_TEXTURE_CALLBACK, + WINED3D_CS_OP_USER_CALLBACK, ++ WINED3D_CS_OP_FENCE, + WINED3D_CS_OP_STOP, + }; + +@@ -482,6 +483,12 @@ struct wined3d_cs_wait_idle + enum wined3d_cs_op opcode; + }; + ++struct wined3d_cs_fence ++{ ++ enum wined3d_cs_op opcode; ++ GLsync *fence; ++}; ++ + struct wined3d_cs_stop + { + enum wined3d_cs_op opcode; +@@ -2803,6 +2810,46 @@ void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + ++static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) ++{ ++ const struct wined3d_cs_fence *op = data; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_context *context; ++ GLsync fence; ++ ++ context = context_acquire(cs->c.device, NULL, 0); ++ gl_info = wined3d_context_gl(context)->gl_info; ++ ++ fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); ++ wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); ++ ++ *op->fence = fence; ++ ++ checkGLcall("fence"); ++ ++ context_release(context); ++} ++ ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++{ ++ struct wined3d_cs_fence *op; ++ GLsync fence; ++ ++ op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); ++ op->opcode = WINED3D_CS_OP_FENCE; ++ op->fence = &fence; ++ ++ cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); ++ cs->c.ops->finish(cs, WINED3D_CS_QUEUE_DEFAULT); ++ ++ return fence; ++} ++ ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++{ ++ return wined3d_cs_emit_fence(cs); ++} ++ + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) + { + struct wined3d_cs_stop *op; +@@ -2918,6 +2965,7 @@ static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void + /* WINED3D_CS_OP_EXECUTE_COMMAND_LIST */ wined3d_cs_exec_execute_command_list, + /* WINED3D_CS_OP_GL_TEXTURE_CALLBACK */ wined3d_cs_exec_gl_texture_callback, + /* WINED3D_CS_OP_USER_CALLBACK */ wined3d_cs_exec_user_callback, ++ /* WINED3D_CS_OP_FENCE */ wined3d_cs_exec_fence, + }; + + static void *wined3d_cs_st_require_space(struct wined3d_device_context *context, +diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c +index 9ec837e837d..9d042f4d0e1 100644 +--- a/dlls/wined3d/device.c ++++ b/dlls/wined3d/device.c +@@ -218,6 +218,8 @@ void wined3d_device_cleanup(struct wined3d_device *device) + if (device->swapchain_count) + wined3d_device_uninit_3d(device); + ++ wined3d_destroy_gl_vr_context(&device->vr_context); ++ + wined3d_cs_destroy(device->cs); + + for (i = 0; i < ARRAY_SIZE(device->multistate_funcs); ++i) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index b8fa5d7d3ae..25c5e57fca0 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4585,6 +4585,129 @@ void CDECL wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_cs_emit_gl_texture_callback(device->cs, texture, callback, depth_texture, data, size); + } + ++static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) ++{ ++ const struct wined3d_adapter *adapter = device->adapter; ++ const struct wined3d_gl_info *gl_info = &adapter->gl_info; ++ struct wined3d_vr_gl_context *ctx = &device->vr_context; ++ PIXELFORMATDESCRIPTOR pfd; ++ int pixel_format; ++ HGLRC share_ctx; ++ ++ if (ctx->gl_info) ++ return gl_info; ++ ++ TRACE("Creating GL context.\n"); ++ ++ if (!gl_info->p_wglCreateContextAttribsARB) ++ { ++ ERR("wglCreateContextAttribsARB is not supported.\n"); ++ return NULL; ++ } ++ ++ if (!gl_info->supported[ARB_SYNC]) ++ { ++ FIXME("ARB_sync is not supported.\n"); ++ return NULL; ++ } ++ ++ ctx->window = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D VR window", ++ WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL); ++ if (!ctx->window) ++ { ++ ERR("Failed to create a window.\n"); ++ return NULL; ++ } ++ ++ ctx->dc = GetDC(ctx->window); ++ if (!ctx->dc) ++ { ++ ERR("Failed to get a DC.\n"); ++ goto fail; ++ } ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd.nSize = sizeof(pfd); ++ pfd.nVersion = 1; ++ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW; ++ pfd.iPixelType = PFD_TYPE_RGBA; ++ pfd.cColorBits = 32; ++ pfd.iLayerType = PFD_MAIN_PLANE; ++ ++ if (!(pixel_format = ChoosePixelFormat(ctx->dc, &pfd))) ++ { ++ ERR("Failed to find a suitable pixel format.\n"); ++ goto fail; ++ } ++ DescribePixelFormat(ctx->dc, pixel_format, sizeof(pfd), &pfd); ++ SetPixelFormat(ctx->dc, pixel_format, &pfd); ++ ++ share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; ++ if (!(ctx->gl_ctx = context_create_wgl_attribs(gl_info, ctx->dc, share_ctx))) ++ { ++ WARN("Failed to create GL context for VR.\n"); ++ goto fail; ++ } ++ ++ if (!wglMakeCurrent(ctx->dc, ctx->gl_ctx)) ++ { ++ ERR("Failed to make GL context current.\n"); ++ goto fail; ++ } ++ ++ checkGLcall("create context"); ++ ++ ctx->gl_info = gl_info; ++ return gl_info; ++ ++fail: ++ if (ctx->gl_ctx) ++ wglDeleteContext(ctx->gl_ctx); ++ ctx->gl_ctx = NULL; ++ if (ctx->dc) ++ ReleaseDC(ctx->window, ctx->dc); ++ ctx->dc = NULL; ++ if (ctx->window) ++ DestroyWindow(ctx->window); ++ ctx->window = NULL; ++ return NULL; ++} ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) ++{ ++ if (!ctx->gl_info) ++ return; ++ ++ TRACE("Destroying GL context.\n"); ++ ++ wglMakeCurrent(NULL, NULL); ++ wglDeleteContext(ctx->gl_ctx); ++ ReleaseDC(ctx->window, ctx->dc); ++ DestroyWindow(ctx->window); ++} ++ ++unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) ++{ ++ struct wined3d_device *device = texture->resource.device; ++ const struct wined3d_gl_info *gl_info; ++ struct wined3d_texture_gl *gl_texture; ++ GLsync fence; ++ ++ TRACE("texture %p.\n", texture); ++ ++ if (!(gl_info = wined3d_prepare_vr_gl_context(device))) ++ return 0; ++ ++ fence = wined3d_cs_synchronize(device->cs); ++ GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); ++ GL_EXTCALL(glDeleteSync(fence)); ++ ++ checkGLcall("synchronize CS"); ++ ++ gl_texture = wined3d_texture_gl(texture); ++ return gl_texture->texture_rgb.name; ++} ++ + static void wined3d_texture_no3d_upload_data(struct wined3d_context *context, + const struct wined3d_const_bo_address *src_bo_addr, const struct wined3d_format *src_format, + const struct wined3d_box *src_box, unsigned int src_row_pitch, unsigned int src_slice_pitch, +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 0a18d909c5e..4bd32d95cda 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry + struct wined3d_stream_output_element elements[1]; + }; + ++struct wined3d_vr_gl_context ++{ ++ HWND window; ++ HDC dc; ++ HGLRC gl_ctx; ++ const struct wined3d_gl_info *gl_info; ++}; ++ + struct wined3d_device + { + LONG ref; +@@ -3842,6 +3850,8 @@ struct wined3d_device + /* Context management */ + struct wined3d_context **contexts; + UINT context_count; ++ ++ struct wined3d_vr_gl_context vr_context; + + CRITICAL_SECTION bo_map_lock; + }; +@@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++ ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++ ++ + static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, + enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + { +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index c5395ccc0ed..b1bda8ada76 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -28,6 +28,8 @@ import "d3d11.idl"; + interface IWineD3D11Texture2D : ID3D11Texture2D + { + void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ ++ unsigned int get_gl_texture(); + } + + typedef void (__cdecl *user_cs_callback)(const void *data, unsigned int data_size); +diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h +index 81235a7677c..d63c54ca593 100644 +--- a/include/wine/wined3d.h ++++ b/include/wine/wined3d.h +@@ -2974,6 +2974,8 @@ typedef void (__cdecl *wined3d_gl_texture_callback)(unsigned int gl_texture, uns + void __cdecl wined3d_access_gl_texture(struct wined3d_texture *texture, + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, const void *data, unsigned int size); + ++unsigned int __cdecl wined3d_get_gl_texture(struct wined3d_texture *texture); ++ + typedef void (__cdecl *wined3d_cs_callback)(const void *data, unsigned int size); + + void __cdecl wined3d_device_run_cs_callback(struct wined3d_device *device, +-- +2.30.2 + +From cc1f77d00ee61e8620029c08110015543682acb8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:04:07 +0200 +Subject: [PATCH 09/12] wined3d: Load TEXTURE_RGB location for synchronous + texture access. + +--- + dlls/wined3d/cs.c | 10 +++++++--- + dlls/wined3d/texture.c | 2 +- + dlls/wined3d/wined3d_private.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c +index 511883924a1..6242651dd72 100644 +--- a/dlls/wined3d/cs.c ++++ b/dlls/wined3d/cs.c +@@ -486,6 +486,7 @@ struct wined3d_cs_wait_idle + struct wined3d_cs_fence + { + enum wined3d_cs_op opcode; ++ struct wined3d_texture *texture; + GLsync *fence; + }; + +@@ -2820,6 +2821,8 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context = context_acquire(cs->c.device, NULL, 0); + gl_info = wined3d_context_gl(context)->gl_info; + ++ wined3d_texture_load_location(op->texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); ++ + fence = GL_EXTCALL(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + wined3d_context_gl(context)->gl_info->gl_ops.gl.p_glFlush(); + +@@ -2830,13 +2833,14 @@ static void wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data) + context_release(context); + } + +-static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) ++static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs, struct wined3d_texture *texture) + { + struct wined3d_cs_fence *op; + GLsync fence; + + op = cs->c.ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_FENCE; ++ op->texture = texture; + op->fence = &fence; + + cs->c.ops->submit(cs, WINED3D_CS_QUEUE_DEFAULT); +@@ -2845,9 +2849,9 @@ static GLsync wined3d_cs_emit_fence(struct wined3d_cs *cs) + return fence; + } + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) + { +- return wined3d_cs_emit_fence(cs); ++ return wined3d_cs_emit_fence(cs, texture); + } + + static void wined3d_cs_emit_stop(struct wined3d_cs *cs) +diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c +index 25c5e57fca0..72a64889ca9 100644 +--- a/dlls/wined3d/texture.c ++++ b/dlls/wined3d/texture.c +@@ -4698,7 +4698,7 @@ unsigned int CDECL wined3d_get_gl_texture(struct wined3d_texture *texture) + if (!(gl_info = wined3d_prepare_vr_gl_context(device))) + return 0; + +- fence = wined3d_cs_synchronize(device->cs); ++ fence = wined3d_cs_synchronize(device->cs, texture); + GL_EXTCALL(glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)); + GL_EXTCALL(glDeleteSync(fence)); + +diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h +index 4bd32d95cda..2a9b2c08877 100644 +--- a/dlls/wined3d/wined3d_private.h ++++ b/dlls/wined3d/wined3d_private.h +@@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t + void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, + wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; + + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + +-- +2.30.2 + +From 29f4110ef25083ad84d3f441986345abf79f3eaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Fri, 21 Sep 2018 12:17:15 +0200 +Subject: [PATCH 10/12] d3d11: Pass IWineD3D11Texture2D to access_gl_texture(). + +--- + dlls/d3d11/texture.c | 21 +++++++-------------- + include/wine/wined3d-interop.idl | 3 ++- + 2 files changed, 9 insertions(+), 15 deletions(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 7f7e8254225..32bf3c3c9cc 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -716,27 +716,20 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + } + + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, +- gl_texture_callback callback, IUnknown *depth_unk, const void *data, unsigned int size) ++ gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { + struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; +- IWineD3D11Texture2D *depth_d3d11 = NULL; ++ struct wined3d_texture *wined3d_depth_texture = NULL; + +- TRACE("iface %p, callback %p, data %p, size %u.\n", iface, callback, data, size); ++ TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", ++ iface, callback, depth_texture, data, size); + + wined3d_mutex_lock(); + +- if (depth_unk) +- { +- HRESULT hr; +- hr = IUnknown_QueryInterface(depth_unk, &IID_IWineD3D11Texture2D, (void**)&depth_d3d11); +- if(hr == S_OK) +- depth_tex = impl_from_IWineD3D11Texture2D(depth_d3d11); +- } +- +- wined3d_access_gl_texture(texture->wined3d_texture, callback, depth_tex ? depth_tex->wined3d_texture : NULL, data, size); ++ if (depth_texture) ++ wined3d_depth_texture = impl_from_IWineD3D11Texture2D(depth_texture)->wined3d_texture; + +- if (depth_d3d11) +- IWineD3D11Texture2D_Release(depth_d3d11); ++ wined3d_access_gl_texture(texture->wined3d_texture, callback, wined3d_depth_texture, data, size); + + wined3d_mutex_unlock(); + } +diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl +index b1bda8ada76..f960e2f6d9d 100644 +--- a/include/wine/wined3d-interop.idl ++++ b/include/wine/wined3d-interop.idl +@@ -27,7 +27,8 @@ import "d3d11.idl"; + ] + interface IWineD3D11Texture2D : ID3D11Texture2D + { +- void access_gl_texture(gl_texture_callback callback, IUnknown *depth_texture, const void *data, unsigned int data_size); ++ void access_gl_texture(gl_texture_callback callback, ++ IWineD3D11Texture2D *depth_texture, const void *data, unsigned int data_size); + + unsigned int get_gl_texture(); + } +-- +2.30.2 + +From 9a97a24ad5001cc4d25b9b8236fbf13b76d9eaef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B3zef=20Kucia?= +Date: Thu, 11 Oct 2018 11:31:43 +0200 +Subject: [PATCH 11/12] d3d11: Remove unused 'depth_tex' variable. + +--- + dlls/d3d11/texture.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c +index 32bf3c3c9cc..f6e7d474d99 100644 +--- a/dlls/d3d11/texture.c ++++ b/dlls/d3d11/texture.c +@@ -718,7 +718,7 @@ static void STDMETHODCALLTYPE d3d11_texture2d_GetDesc(IWineD3D11Texture2D *iface + static void STDMETHODCALLTYPE d3d11_texture2d_access_gl_texture(IWineD3D11Texture2D *iface, + gl_texture_callback callback, IWineD3D11Texture2D *depth_texture, const void *data, unsigned int size) + { +- struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface), *depth_tex = NULL; ++ struct d3d_texture2d *texture = impl_from_IWineD3D11Texture2D(iface); + struct wined3d_texture *wined3d_depth_texture = NULL; + + TRACE("iface %p, callback %p, depth_texture %p, data %p, size %u.\n", +-- +2.30.2 + +From b476c31f53be82ad3b8a3ec9d7987f75e8040cfe Mon Sep 17 00:00:00 2001 +From: GloriousEggroll +Date: Sun, 18 Aug 2019 19:05:52 -0600 +Subject: [PATCH 12/12] wined3d + +--- + dlls/wined3d/wined3d.spec | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec +index 2879e1653d3..bbc9ce7b99c 100644 +--- a/dlls/wined3d/wined3d.spec ++++ b/dlls/wined3d/wined3d.spec +@@ -367,6 +367,11 @@ + @ cdecl wined3d_vertex_declaration_decref(ptr) + @ cdecl wined3d_vertex_declaration_get_parent(ptr) + @ cdecl wined3d_vertex_declaration_incref(ptr) ++ ++@ cdecl wined3d_access_gl_texture(ptr ptr ptr long) ++@ cdecl wined3d_device_run_cs_callback(ptr ptr ptr long) ++@ cdecl wined3d_device_wait_idle(ptr) ++@ cdecl wined3d_get_gl_texture(ptr) + + @ cdecl vkd3d_create_instance(ptr ptr) + @ cdecl vkd3d_instance_decref(ptr) +-- +2.30.2 + diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr index e6b60668d..933bbd545 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr @@ -34,8 +34,18 @@ fi fi else - if git merge-base --is-ancestor 7d20333faf8e93a3a16680cf1beadc9cdcb50c45 HEAD; then + if git merge-base --is-ancestor 8f5aa334dac0118905c45b84ab95661887a689fa HEAD; then _patchname='proton-vr.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher + elif git merge-base --is-ancestor 5bc6ab56d0eefd388b3ae37c7941cd118bd672da HEAD; then + _patchname='proton-vr-8f5aa33.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher + elif git merge-base --is-ancestor c065b4fe0b3622e0c737a4c7c1c514273ad2d8a7 HEAD; then + _patchname='proton-vr-5bc6ab5.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher + elif git merge-base --is-ancestor 4413e94908bfd561c759b121a7f460b51677cf0b HEAD; then + _patchname='proton-vr-c065b4f.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher + elif git merge-base --is-ancestor 890877f14574e45b5b30d9bcc93d30842b7d0065 HEAD; then + _patchname='proton-vr-4413e94.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher + elif git merge-base --is-ancestor 7d20333faf8e93a3a16680cf1beadc9cdcb50c45 HEAD; then + _patchname='proton-vr-890877f.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher elif git merge-base --is-ancestor 872dc83e81c942d17a39700c4b520a6e92ddbdf2 HEAD; then _patchname='proton-vr-7d20333.patch' && _patchmsg="Enable Proton vr-related wined3d additions" && nonuser_patcher elif git merge-base --is-ancestor f6f66661b3fabdcd6cf2f4999b3029e72d492fa4 HEAD; then diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr.patch index 9948ea861..3541c0d2a 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-vr/proton-vr.patch @@ -121,11 +121,11 @@ index f5b1a078907..756d47ef20d 100644 } +void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, -+ wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_gl_texture_callback callback, const void *data, unsigned int size); + - static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, - enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) - { + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index dd1c15f14ed..2123e857a52 100644 --- a/include/wine/wined3d.h @@ -252,12 +252,12 @@ index 756d47ef20d..602647b1e94 100644 @@ -4764,6 +4764,8 @@ static inline void wined3d_cs_finish(struct wined3d_cs *cs, enum wined3d_cs_queu void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, - wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + wined3d_gl_texture_callback callback, const void *data, unsigned int size); +void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, -+ wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ wined3d_cs_callback callback, const void *data, unsigned int size); - static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, - enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 2123e857a52..39689bdb4e1 100644 --- a/include/wine/wined3d.h @@ -301,9 +301,9 @@ index 1a8cdc6d77c..2b1809726ca 100644 +#include "wine/wined3d-interop.h" + - #define MAKE_TAG(ch0, ch1, ch2, ch3) \ - ((DWORD)(ch0) | ((DWORD)(ch1) << 8) | \ - ((DWORD)(ch2) << 16) | ((DWORD)(ch3) << 24 )) + struct d3d_device; + + /* TRACE helper functions */ @@ -144,7 +146,7 @@ struct d3d_texture1d *unsafe_impl_from_ID3D10Texture1D(ID3D10Texture1D *iface) D /* ID3D11Texture2D, ID3D10Texture2D */ struct d3d_texture2d @@ -327,14 +327,14 @@ index 0819918b2a1..41967dd0a1c 100644 return S_OK; } @@ -6594,7 +6594,7 @@ static HRESULT CDECL device_parent_create_swapchain_texture(struct wined3d_devic - - *wined3d_texture = texture->wined3d_texture; - wined3d_texture_incref(*wined3d_texture); -- ID3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); -+ IWineD3D11Texture2D_Release(&texture->ID3D11Texture2D_iface); - - return S_OK; + return hr; + + hr = IUnknown_QueryInterface(object->dxgi_resource, &IID_IDXGISurface, (void **)ret_surface); +- ID3D11Texture2D_Release(&object->ID3D11Texture2D_iface); ++ IWineD3D11Texture2D_Release(&object->ID3D11Texture2D_iface); + return hr; } + diff --git a/dlls/d3d11/texture.c b/dlls/d3d11/texture.c index 061bbb09795..dd21c6d7f08 100644 --- a/dlls/d3d11/texture.c @@ -792,13 +792,13 @@ index 602647b1e94..8a38f6d4925 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -4766,6 +4766,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t - wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + wined3d_gl_texture_callback callback, const void *data, unsigned int size); void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, - wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; -+void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + wined3d_cs_callback callback, const void *data, unsigned int size); ++void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs); - static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, - enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl index d5c91623b3c..6f8ea3770e3 100644 --- a/include/wine/wined3d-interop.idl @@ -957,12 +957,12 @@ index 8a38f6d4925..a01ee1690c1 100644 } void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_texture *texture, -- wined3d_gl_texture_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; +- wined3d_gl_texture_callback callback, const void *data, unsigned int size); + wined3d_gl_texture_callback callback, struct wined3d_texture *depth_texture, -+ const void *data, unsigned int size) DECLSPEC_HIDDEN; ++ const void *data, unsigned int size); void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, - wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; - void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + wined3d_cs_callback callback, const void *data, unsigned int size); + void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs); diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl index 6f8ea3770e3..c5395ccc0ed 100644 --- a/include/wine/wined3d-interop.idl @@ -1056,13 +1056,13 @@ index a01ee1690c1..0a18d909c5e 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -4767,7 +4767,6 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t - const void *data, unsigned int size) DECLSPEC_HIDDEN; + const void *data, unsigned int size); void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, - wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; --void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs) DECLSPEC_HIDDEN; + wined3d_cs_callback callback, const void *data, unsigned int size); +-void wined3d_cs_emit_wait_idle(struct wined3d_cs *cs); - static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, - enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, -- 2.30.2 @@ -1219,7 +1219,7 @@ index b8fa5d7d3ae..25c5e57fca0 100644 +static const struct wined3d_gl_info *wined3d_prepare_vr_gl_context(struct wined3d_device *device) +{ + const struct wined3d_adapter *adapter = device->adapter; -+ const struct wined3d_gl_info *gl_info = &adapter->gl_info; ++ const struct wined3d_gl_info *gl_info = &wined3d_adapter_gl_const(adapter)->gl_info; + struct wined3d_vr_gl_context *ctx = &device->vr_context; + PIXELFORMATDESCRIPTOR pfd; + int pixel_format; @@ -1346,6 +1346,14 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 0a18d909c5e..4bd32d95cda 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h +@@ -50,6 +50,7 @@ + #include "wine/wined3d.h" + #include "wine/list.h" + #include "wine/rbtree.h" ++#include "wine/wgl.h" + + static inline size_t align(size_t addr, size_t alignment) + { @@ -3774,6 +3774,14 @@ struct wined3d_so_desc_entry struct wined3d_stream_output_element elements[1]; }; @@ -1372,16 +1380,16 @@ index 0a18d909c5e..4bd32d95cda 100644 }; @@ -4768,6 +4778,11 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, - wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + wined3d_cs_callback callback, const void *data, unsigned int size); -+GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs); + -+void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; ++void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx); + + - static inline void wined3d_device_context_push_constants(struct wined3d_device_context *context, - enum wined3d_push_constants p, unsigned int start_idx, unsigned int count, const void *constants) - { + void wined3d_device_context_emit_blt_sub_resource(struct wined3d_device_context *context, + struct wined3d_resource *dst_resource, unsigned int dst_sub_resource_idx, const struct wined3d_box *dst_box, + struct wined3d_resource *src_resource, unsigned int src_sub_resource_idx, const struct wined3d_box *src_box, diff --git a/include/wine/wined3d-interop.idl b/include/wine/wined3d-interop.idl index c5395ccc0ed..b1bda8ada76 100644 --- a/include/wine/wined3d-interop.idl @@ -1491,12 +1499,12 @@ index 4bd32d95cda..2a9b2c08877 100644 +++ b/dlls/wined3d/wined3d_private.h @@ -4778,7 +4778,7 @@ void wined3d_cs_emit_gl_texture_callback(struct wined3d_cs *cs, struct wined3d_t void wined3d_cs_emit_user_callback(struct wined3d_cs *cs, - wined3d_cs_callback callback, const void *data, unsigned int size) DECLSPEC_HIDDEN; + wined3d_cs_callback callback, const void *data, unsigned int size); --GLsync wined3d_cs_synchronize(struct wined3d_cs *cs) DECLSPEC_HIDDEN; -+GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture) DECLSPEC_HIDDEN; +-GLsync wined3d_cs_synchronize(struct wined3d_cs *cs); ++GLsync wined3d_cs_synchronize(struct wined3d_cs *cs, struct wined3d_texture *texture); - void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx) DECLSPEC_HIDDEN; + void wined3d_destroy_gl_vr_context(struct wined3d_vr_gl_context *ctx); -- 2.30.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/legacy/proton_battleye-acad495.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/legacy/proton_battleye-acad495.patch new file mode 100644 index 000000000..81b3de6fe --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/legacy/proton_battleye-acad495.patch @@ -0,0 +1,737 @@ +From f7b8a83d06870a54430481c85c764401d96e143c Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Fri, 26 Mar 2021 10:48:14 -0400 +Subject: [PATCH] ntdll: Try to load builtin DLLs from Battleye Runtime + directory. + +Signed-off-by: Derek Lesho + +CW-Bug-Id: #16650 +--- + dlls/ntdll/unix/loader.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 0a05fb3f10b..711b7224bfc 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -526,11 +526,14 @@ static const char *get_pe_dir( WORD machine ) + + static void set_dll_path(void) + { +- char *p, *path = getenv( "WINEDLLPATH" ); ++ char *p, *path = getenv( "WINEDLLPATH" ), *be_runtime = getenv( "PROTON_BATTLEYE_RUNTIME" ); + int i, count = 0; + + if (path) for (p = path, count = 1; *p; p++) if (*p == ':') count++; + ++ if (be_runtime) ++ count += 2; ++ + dll_paths = malloc( (count + 2) * sizeof(*dll_paths) ); + count = 0; + +@@ -543,6 +546,24 @@ static void set_dll_path(void) + free( path ); + } + ++ if (be_runtime) ++ { ++ const char lib32[] = "/v1/lib/wine/"; ++ const char lib64[] = "/v1/lib64/wine/"; ++ ++ p = malloc( strlen(be_runtime) + strlen(lib32) + 1 ); ++ strcpy(p, be_runtime); ++ strcat(p, lib32); ++ ++ dll_paths[count++] = p; ++ ++ p = malloc( strlen(be_runtime) + strlen(lib64) + 1 ); ++ strcpy(p, be_runtime); ++ strcat(p, lib64); ++ ++ dll_paths[count++] = p; ++ } ++ + for (i = 0; i < count; i++) dll_path_maxlen = max( dll_path_maxlen, strlen(dll_paths[i]) ); + dll_paths[count] = NULL; + } +From 1e89f3ebc36d9a867b71b0f8f9aab516a6be90a1 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Tue, 30 Mar 2021 21:45:05 -0400 +Subject: [PATCH] kernelbase: Redirect BattlEye Launcher process creation to + game executable. + +Signed-off-by: Derek Lesho + +CW-Bug-Id: #16650 +--- + dlls/kernelbase/process.c | 200 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 200 insertions(+) + +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 08dad9cb646..b2a49a16177 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -28,6 +28,7 @@ + #include "windef.h" + #include "winbase.h" + #include "winnls.h" ++#include "winver.h" + #include "wincontypes.h" + #include "winternl.h" + +@@ -486,6 +487,197 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalA( HANDLE token, const char * + return ret; + } + ++static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_name, DWORD new_name_len, WCHAR **cmd_line) ++{ ++ WCHAR full_path[MAX_PATH], config_path[MAX_PATH]; ++ WCHAR *p; ++ DWORD size; ++ void *block; ++ DWORD *translation; ++ char buf[100]; ++ char *product_name; ++ int launcher_exe_len, game_exe_len, arg_len; ++ HANDLE launcher_cfg; ++ LARGE_INTEGER launcher_cfg_size; ++ char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe, *be_arg = NULL; ++ BOOL wow64; ++ WCHAR *new_cmd_line; ++ ++ if (!GetLongPathNameW( app_name, full_path, MAX_PATH )) lstrcpynW( full_path, app_name, MAX_PATH ); ++ if (!GetFullPathNameW( full_path, MAX_PATH, full_path, NULL )) lstrcpynW( full_path, app_name, MAX_PATH ); ++ ++ /* We detect the BattlEye launcher executable through the product name property, as the executable name varies */ ++ size = GetFileVersionInfoSizeExW(0, full_path, NULL); ++ if (!size) ++ return 0; ++ ++ block = HeapAlloc( GetProcessHeap(), 0, size ); ++ ++ if (!GetFileVersionInfoExW(0, full_path, 0, size, block)) ++ { ++ HeapFree( GetProcessHeap(), 0, block ); ++ return 0; ++ } ++ ++ if (!VerQueryValueA(block, "\\VarFileInfo\\Translation", (void **) &translation, &size) || size != 4) ++ { ++ HeapFree( GetProcessHeap(), 0, block ); ++ return 0; ++ } ++ ++ sprintf(buf, "\\StringFileInfo\\%08x\\ProductName", MAKELONG(HIWORD(*translation), LOWORD(*translation))); ++ ++ if (!VerQueryValueA(block, buf, (void **) &product_name, &size)) ++ { ++ HeapFree( GetProcessHeap(), 0, block ); ++ return 0; ++ } ++ ++ if (strcmp(product_name, "BattlEye Launcher")) ++ { ++ HeapFree( GetProcessHeap(), 0, block); ++ return 0; ++ } ++ ++ HeapFree( GetProcessHeap(), 0, block ); ++ ++ TRACE("Detected launch of a BattlEye Launcher, attempting to launch game executable instead.\n"); ++ ++ lstrcpynW(config_path, full_path, MAX_PATH); ++ ++ for (p = config_path + wcslen(config_path); p != config_path; --p) ++ if (*p == '\\') break; ++ ++ if (*p == '\\') ++ { ++ *p = 0; ++ launcher_exe_len = wcslen(p + 1); ++ } ++ else ++ launcher_exe_len = wcslen(full_path); ++ ++ lstrcatW(config_path, L"\\BattlEye\\BELauncher.ini"); ++ ++ launcher_cfg = CreateFileW(config_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (launcher_cfg == INVALID_HANDLE_VALUE) ++ return 0; ++ ++ if(!GetFileSizeEx(launcher_cfg, &launcher_cfg_size) || launcher_cfg_size.u.HighPart) ++ { ++ CloseHandle(launcher_cfg); ++ return 0; ++ } ++ ++ configs = HeapAlloc( GetProcessHeap(), 0, launcher_cfg_size.u.LowPart); ++ ++ if (!ReadFile(launcher_cfg, configs, launcher_cfg_size.u.LowPart, &size, NULL) || size != launcher_cfg_size.u.LowPart) ++ { ++ CloseHandle(launcher_cfg); ++ HeapFree( GetProcessHeap(), 0, configs ); ++ return 0; ++ } ++ ++ CloseHandle(launcher_cfg); ++ ++ config = configs; ++ do ++ { ++ if (!strncmp(config, "32BitExe=", 9)) ++ arch_32_exe = config + 9; ++ ++ if (!strncmp(config, "64BitExe=", 9)) ++ arch_64_exe = config + 9; ++ ++ if (!strncmp(config, "BEArg=", 6)) ++ be_arg = config + 6; ++ } ++ while ((config = strchr(config, '\n')) && *(config++)); ++ ++ if (arch_64_exe && (sizeof(void *) == 8 || (IsWow64Process(GetCurrentProcess(), &wow64) && wow64))) ++ game_exe = arch_64_exe; ++ else if (arch_32_exe) ++ game_exe = arch_32_exe; ++ else ++ { ++ HeapFree( GetProcessHeap(), 0, configs ); ++ WARN("Failed to find game executable name from BattlEye config.\n"); ++ return 0; ++ } ++ ++ if (strchr(game_exe, '\r')) ++ *(strchr(game_exe, '\r')) = 0; ++ if (strchr(game_exe, '\n')) ++ *(strchr(game_exe, '\n')) = 0; ++ game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; ++ ++ if (be_arg) ++ { ++ if (strchr(be_arg, '\r')) ++ *(strchr(be_arg, '\r')) = 0; ++ if (strchr(be_arg, '\n')) ++ *(strchr(be_arg, '\n')) = 0; ++ arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; ++ } ++ ++ TRACE("Launching game executable %s for BattlEye.\n", game_exe); ++ ++ if ((wcslen(app_name) - launcher_exe_len) + game_exe_len + 1 > new_name_len) ++ { ++ HeapFree( GetProcessHeap(), 0, configs ); ++ WARN("Game executable path doesn't fit in buffer.\n"); ++ return 0; ++ } ++ ++ wcscpy(new_name, app_name); ++ p = new_name + wcslen(new_name) - launcher_exe_len; ++ MultiByteToWideChar(CP_ACP, 0, game_exe, -1, p, game_exe_len + 1); ++ ++ /* find and replace executable name in command line, and add BE argument */ ++ p = *cmd_line; ++ if (p[0] == '\"') ++ p++; ++ ++ if (!wcsncmp(p, app_name, wcslen(app_name))) ++ p += wcslen(app_name) - launcher_exe_len; ++ else ++ p = NULL; ++ ++ if (p || be_arg) ++ { ++ size = wcslen(*cmd_line) + 1; ++ if (p) ++ size += game_exe_len - launcher_exe_len; ++ if (be_arg) ++ size += 1 /* space */ + arg_len; ++ size *= sizeof(WCHAR); ++ ++ /* freed by parent function */ ++ new_cmd_line = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); ++ ++ if (p) ++ { ++ lstrcpynW(new_cmd_line, *cmd_line, p - *cmd_line); ++ MultiByteToWideChar(CP_ACP, 0, game_exe, -1, new_cmd_line + wcslen(new_cmd_line), game_exe_len + 1); ++ wcscat(new_cmd_line, p + launcher_exe_len); ++ } ++ else ++ { ++ wcscpy(new_cmd_line, *cmd_line); ++ } ++ ++ if (be_arg) ++ { ++ wcscat(new_cmd_line, L" "); ++ MultiByteToWideChar(CP_ACP, 0, be_arg, -1, new_cmd_line + wcslen(new_cmd_line), arg_len + 1); ++ } ++ ++ *cmd_line = new_cmd_line; ++ } ++ ++ HeapFree( GetProcessHeap(), 0, configs ); ++ return 1; ++} ++ + /********************************************************************** + * CreateProcessInternalW (kernelbase.@) + */ +@@ -550,6 +742,14 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR + app_name = name; + } + ++ p = tidy_cmdline; ++ if (battleye_launcher_redirect_hack( app_name, name, ARRAY_SIZE(name), &tidy_cmdline )) ++ { ++ app_name = name; ++ if (p != tidy_cmdline && p != cmd_line) ++ HeapFree( GetProcessHeap(), 0, p ); ++ } ++ + /* Warn if unsupported features are used */ + + if (flags & (IDLE_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS | +From 66e87df57525a8421caa75aad573663f90534fb9 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Thu, 30 Sep 2021 14:38:33 +0200 +Subject: [PATCH] sechost: Fake presence of BEService service for ARK: Survival + Evolved. + +The game uses the presence and status of BEService to determine whether or not the game is running in BattlEye. Since with the Proton Bridge we don't have a dedicated background service, we can just pretend the service is always running. + +CW-Bug-Id: #16650 +--- + dlls/sechost/service.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c +index e6f4eb75db0..d005b64ed19 100644 +--- a/dlls/sechost/service.c ++++ b/dlls/sechost/service.c +@@ -314,6 +314,8 @@ SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenServiceW( SC_HANDLE manager, const WCHAR + SC_RPC_HANDLE handle = NULL; + DWORD err; + ++ char str[64]; ++ + TRACE( "%p %s %#lx\n", manager, debugstr_w(name), access ); + + if (!manager) +@@ -322,6 +324,14 @@ SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenServiceW( SC_HANDLE manager, const WCHAR + return NULL; + } + ++ /* HACK for ARK: Survivial Evolved checking the status of BEService to determine whether BE is enabled. */ ++ if(GetEnvironmentVariableA("SteamGameId", str, sizeof(str)) && !strcmp(str, "346110") && ++ !wcscmp(name, L"BEService")) ++ { ++ WARN("HACK: returning fake service handle for BEService.\n"); ++ return (void *)0xdeadbeef; ++ } ++ + __TRY + { + err = svcctl_OpenServiceW( manager, name, access, &handle ); +@@ -1106,6 +1116,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceStatusEx( SC_HANDLE service, SC_STATUS + { + DWORD err; + ++ char str[64]; ++ + TRACE( "%p %d %p %ld %p\n", service, level, buffer, size, ret_size ); + + if (level != SC_STATUS_PROCESS_INFO) return set_error( ERROR_INVALID_LEVEL ); +@@ -1116,6 +1128,24 @@ BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceStatusEx( SC_HANDLE service, SC_STATUS + return set_error( ERROR_INSUFFICIENT_BUFFER ); + } + ++ /* HACK for ARK: Survivial Evolved checking the status of BEService to determine whether BE is enabled. */ ++ if(GetEnvironmentVariableA("SteamGameId", str, sizeof(str)) && !strcmp(str, "346110") && ++ service == (void *)0xdeadbeef) ++ { ++ SERVICE_STATUS_PROCESS *status = (SERVICE_STATUS_PROCESS *)buffer; ++ WARN("HACK: returning fake data for BEService.\n"); ++ status->dwServiceType = SERVICE_WIN32_OWN_PROCESS; ++ status->dwCurrentState = SERVICE_RUNNING; ++ status->dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; ++ status->dwWin32ExitCode = NO_ERROR; ++ status->dwServiceSpecificExitCode = 0; ++ status->dwCheckPoint = 0; ++ status->dwWaitHint = 0; ++ status->dwProcessId = 0xdeadbee0; ++ status->dwServiceFlags = 0; ++ return TRUE; ++ } ++ + __TRY + { + err = svcctl_QueryServiceStatusEx( service, level, buffer, size, ret_size ); +From c1d58f41052ae7ca24a83983bc9dd8ce4a395c72 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Fri, 21 Jan 2022 14:40:43 -0500 +Subject: [PATCH] battleye: Add launcher process instead of redirecting + CreateProcess call. + +Fixes Arma 3 Launcher CW-Bug-Id: #18934 +--- + configure.ac | 1 + + dlls/kernelbase/process.c | 134 ++++---------------------------- + programs/belauncher/Makefile.in | 7 ++ + programs/belauncher/main.c | 116 +++++++++++++++++++++++++++ + 4 files changed, 139 insertions(+), 119 deletions(-) + create mode 100644 programs/belauncher/Makefile.in + create mode 100644 programs/belauncher/main.c + +diff --git a/configure.ac b/configure.ac +index 8e4462b1d4c..bc4f47d9889 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3579,6 +3579,7 @@ WINE_CONFIG_MAKEFILE(po) + WINE_CONFIG_MAKEFILE(programs/arp) + WINE_CONFIG_MAKEFILE(programs/aspnet_regiis) + WINE_CONFIG_MAKEFILE(programs/attrib) ++WINE_CONFIG_MAKEFILE(programs/belauncher) + WINE_CONFIG_MAKEFILE(programs/cabarc) + WINE_CONFIG_MAKEFILE(programs/cacls) + WINE_CONFIG_MAKEFILE(programs/chcp.com) +diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c +index 22f9115011e..01f4ae08721 100644 +--- a/dlls/kernelbase/process.c ++++ b/dlls/kernelbase/process.c +@@ -489,18 +489,15 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalA( HANDLE token, const char * + + static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_name, DWORD new_name_len, WCHAR **cmd_line) + { +- WCHAR full_path[MAX_PATH], config_path[MAX_PATH]; ++ static const WCHAR belauncherW[] = L"c:\\windows\\system32\\belauncher.exe"; ++ ++ WCHAR full_path[MAX_PATH]; + WCHAR *p; + DWORD size; + void *block; + DWORD *translation; + char buf[100]; + char *product_name; +- int launcher_exe_len, game_exe_len, arg_len; +- HANDLE launcher_cfg; +- LARGE_INTEGER launcher_cfg_size; +- char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe, *be_arg = NULL; +- BOOL wow64; + WCHAR *new_cmd_line; + + if (!GetLongPathNameW( app_name, full_path, MAX_PATH )) lstrcpynW( full_path, app_name, MAX_PATH ); +@@ -541,96 +538,15 @@ static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_nam + + HeapFree( GetProcessHeap(), 0, block ); + +- TRACE("Detected launch of a BattlEye Launcher, attempting to launch game executable instead.\n"); +- +- lstrcpynW(config_path, full_path, MAX_PATH); +- +- for (p = config_path + wcslen(config_path); p != config_path; --p) +- if (*p == '\\') break; +- +- if (*p == '\\') +- { +- *p = 0; +- launcher_exe_len = wcslen(p + 1); +- } +- else +- launcher_exe_len = wcslen(full_path); +- +- lstrcatW(config_path, L"\\BattlEye\\BELauncher.ini"); +- +- launcher_cfg = CreateFileW(config_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +- if (launcher_cfg == INVALID_HANDLE_VALUE) +- return 0; +- +- if(!GetFileSizeEx(launcher_cfg, &launcher_cfg_size) || launcher_cfg_size.u.HighPart) +- { +- CloseHandle(launcher_cfg); +- return 0; +- } +- +- configs = HeapAlloc( GetProcessHeap(), 0, launcher_cfg_size.u.LowPart); +- +- if (!ReadFile(launcher_cfg, configs, launcher_cfg_size.u.LowPart, &size, NULL) || size != launcher_cfg_size.u.LowPart) +- { +- CloseHandle(launcher_cfg); +- HeapFree( GetProcessHeap(), 0, configs ); +- return 0; +- } +- +- CloseHandle(launcher_cfg); +- +- config = configs; +- do +- { +- if (!strncmp(config, "32BitExe=", 9)) +- arch_32_exe = config + 9; +- +- if (!strncmp(config, "64BitExe=", 9)) +- arch_64_exe = config + 9; +- +- if (!strncmp(config, "BEArg=", 6)) +- be_arg = config + 6; +- } +- while ((config = strchr(config, '\n')) && *(config++)); +- +- if (arch_64_exe && (sizeof(void *) == 8 || (IsWow64Process(GetCurrentProcess(), &wow64) && wow64))) +- game_exe = arch_64_exe; +- else if (arch_32_exe) +- game_exe = arch_32_exe; +- else +- { +- HeapFree( GetProcessHeap(), 0, configs ); +- WARN("Failed to find game executable name from BattlEye config.\n"); +- return 0; +- } +- +- if (strchr(game_exe, '\r')) +- *(strchr(game_exe, '\r')) = 0; +- if (strchr(game_exe, '\n')) +- *(strchr(game_exe, '\n')) = 0; +- game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; +- +- if (be_arg) +- { +- if (strchr(be_arg, '\r')) +- *(strchr(be_arg, '\r')) = 0; +- if (strchr(be_arg, '\n')) +- *(strchr(be_arg, '\n')) = 0; +- arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; +- } +- +- TRACE("Launching game executable %s for BattlEye.\n", game_exe); ++ TRACE("Detected launch of a BattlEye Launcher, redirecting to Proton version.\n"); + +- if ((wcslen(app_name) - launcher_exe_len) + game_exe_len + 1 > new_name_len) ++ if (new_name_len < wcslen(belauncherW) + 1) + { +- HeapFree( GetProcessHeap(), 0, configs ); + WARN("Game executable path doesn't fit in buffer.\n"); + return 0; + } + +- wcscpy(new_name, app_name); +- p = new_name + wcslen(new_name) - launcher_exe_len; +- MultiByteToWideChar(CP_ACP, 0, game_exe, -1, p, game_exe_len + 1); ++ wcscpy(new_name, belauncherW); + + /* find and replace executable name in command line, and add BE argument */ + p = *cmd_line; +@@ -638,43 +554,23 @@ static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_nam + p++; + + if (!wcsncmp(p, app_name, wcslen(app_name))) +- p += wcslen(app_name) - launcher_exe_len; +- else +- p = NULL; +- +- if (p || be_arg) + { +- size = wcslen(*cmd_line) + 1; +- if (p) +- size += game_exe_len - launcher_exe_len; +- if (be_arg) +- size += 1 /* space */ + arg_len; +- size *= sizeof(WCHAR); ++ new_cmd_line = HeapAlloc( GetProcessHeap(), 0, ( wcslen(*cmd_line) + wcslen(belauncherW) + 1 - wcslen(app_name) ) * sizeof(WCHAR) ); + +- /* freed by parent function */ +- new_cmd_line = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); ++ wcscpy(new_cmd_line, *cmd_line); ++ p = new_cmd_line; ++ if (p[0] == '\"') ++ p++; + +- if (p) +- { +- lstrcpynW(new_cmd_line, *cmd_line, p - *cmd_line); +- MultiByteToWideChar(CP_ACP, 0, game_exe, -1, new_cmd_line + wcslen(new_cmd_line), game_exe_len + 1); +- wcscat(new_cmd_line, p + launcher_exe_len); +- } +- else +- { +- wcscpy(new_cmd_line, *cmd_line); +- } ++ memmove( p + wcslen(belauncherW), p + wcslen(app_name), (wcslen(p) - wcslen(belauncherW)) * sizeof(WCHAR) ); ++ memcpy( p, belauncherW, wcslen(belauncherW) * sizeof(WCHAR) ); + +- if (be_arg) +- { +- wcscat(new_cmd_line, L" "); +- MultiByteToWideChar(CP_ACP, 0, be_arg, -1, new_cmd_line + wcslen(new_cmd_line), arg_len + 1); +- } ++ TRACE("old command line %s.\n", debugstr_w(*cmd_line)); ++ TRACE("new command line %s.\n", debugstr_w(new_cmd_line)); + + *cmd_line = new_cmd_line; + } + +- HeapFree( GetProcessHeap(), 0, configs ); + return 1; + } + +diff --git a/programs/belauncher/Makefile.in b/programs/belauncher/Makefile.in +new file mode 100644 +index 00000000000..f2dc59b07ce +--- /dev/null ++++ b/programs/belauncher/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = belauncher.exe ++IMPORTS = ++ ++EXTRADLLFLAGS = -mwindows -municode ++ ++C_SRCS = \ ++ main.c \ +diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c +new file mode 100644 +index 00000000000..0f727f3ca7d +--- /dev/null ++++ b/programs/belauncher/main.c +@@ -0,0 +1,116 @@ ++#define WIN32_LEAN_AND_MEAN ++#include ++ ++#include "wine/debug.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(belauncher); ++ ++int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow) ++{ ++ char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe, *be_arg = NULL; ++ LARGE_INTEGER launcher_cfg_size; ++ unsigned char battleye_status; ++ int game_exe_len, arg_len; ++ PROCESS_INFORMATION pi; ++ HANDLE launcher_cfg; ++ LPWSTR launch_cmd; ++ STARTUPINFOW si = {0}; ++ DWORD size; ++ BOOL wow64; ++ ++ battleye_status = 0x3; /* Starting */ ++ _write(1, &battleye_status, 1); ++ ++ launcher_cfg = CreateFileW(L"Battleye\\BELauncher.ini", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ++ if (launcher_cfg == INVALID_HANDLE_VALUE) ++ goto start_failed; ++ ++ if(!GetFileSizeEx(launcher_cfg, &launcher_cfg_size) || launcher_cfg_size.u.HighPart) ++ { ++ CloseHandle(launcher_cfg); ++ goto start_failed; ++ } ++ ++ configs = HeapAlloc( GetProcessHeap(), 0, launcher_cfg_size.u.LowPart); ++ ++ if (!ReadFile(launcher_cfg, configs, launcher_cfg_size.u.LowPart, &size, NULL) || size != launcher_cfg_size.u.LowPart) ++ { ++ CloseHandle(launcher_cfg); ++ HeapFree( GetProcessHeap(), 0, configs ); ++ goto start_failed; ++ } ++ ++ CloseHandle(launcher_cfg); ++ ++ config = configs; ++ do ++ { ++ if (!strncmp(config, "32BitExe=", 9)) ++ arch_32_exe = config + 9; ++ ++ if (!strncmp(config, "64BitExe=", 9)) ++ arch_64_exe = config + 9; ++ ++ if (!strncmp(config, "BEArg=", 6)) ++ be_arg = config + 6; ++ } ++ while ((config = strchr(config, '\n')) && *(config++)); ++ ++ if (arch_64_exe && (sizeof(void *) == 8 || (IsWow64Process(GetCurrentProcess(), &wow64) && wow64))) ++ game_exe = arch_64_exe; ++ else if (arch_32_exe) ++ game_exe = arch_32_exe; ++ else ++ { ++ HeapFree( GetProcessHeap(), 0, configs ); ++ WINE_WARN("Failed to find game executable name from BattlEye config.\n"); ++ goto start_failed; ++ } ++ ++ if (strchr(game_exe, '\r')) ++ *(strchr(game_exe, '\r')) = 0; ++ if (strchr(game_exe, '\n')) ++ *(strchr(game_exe, '\n')) = 0; ++ game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; ++ ++ if (be_arg) ++ { ++ if (strchr(be_arg, '\r')) ++ *(strchr(be_arg, '\r')) = 0; ++ if (strchr(be_arg, '\n')) ++ *(strchr(be_arg, '\n')) = 0; ++ arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; ++ } ++ ++ WINE_TRACE("Launching game executable %s for BattlEye.\n", game_exe); ++ battleye_status = 0x9; /* Launching Game */ ++ _write(1, &battleye_status, 1); ++ ++ launch_cmd = HeapAlloc(GetProcessHeap(), 0, (game_exe_len + 1 + wcslen(cmdline) + 1 + arg_len + 1) * sizeof(WCHAR)); ++ MultiByteToWideChar(CP_ACP, 0, game_exe, -1, launch_cmd, game_exe_len + 1); ++ launch_cmd[game_exe_len] = ' '; ++ ++ wcscpy(launch_cmd + game_exe_len + 1, cmdline); ++ launch_cmd[game_exe_len + 1 + wcslen(cmdline)] = ' '; ++ ++ MultiByteToWideChar(CP_ACP, 0, be_arg, -1, launch_cmd + game_exe_len + 1 + wcslen(cmdline) + 1, arg_len + 1); ++ ++ if (!CreateProcessW(NULL, launch_cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) ++ { ++ battleye_status = 0xA; /* Launch Failed */ ++ _write(1, &battleye_status, 1); ++ ++ HeapFree( GetProcessHeap(), 0, launch_cmd ); ++ return GetLastError(); ++ } ++ HeapFree( GetProcessHeap(), 0, launch_cmd ); ++ ++ WaitForSingleObject(pi.hProcess, INFINITE); ++ CloseHandle(pi.hProcess); ++ return 0; ++ ++start_failed: ++ battleye_status = 0x4; /* Start Failed */ ++ _write(1, &battleye_status, 1); ++ return 0; ++} +From 0de1a4cb50828f0867253d2fb3d6d679d4b8a39d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?R=C3=A9mi=20Bernon?= +Date: Tue, 15 Feb 2022 10:56:31 +0100 +Subject: [PATCH] fixup! battleye: Add launcher process instead of redirecting + CreateProcess call. + +--- + programs/belauncher/main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c +index 0f727f3ca7d..86ec46ecee4 100644 +--- a/programs/belauncher/main.c ++++ b/programs/belauncher/main.c +@@ -73,7 +73,8 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cm + *(strchr(game_exe, '\n')) = 0; + game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; + +- if (be_arg) ++ if (!be_arg) arg_len = 0; ++ else + { + if (strchr(be_arg, '\r')) + *(strchr(be_arg, '\r')) = 0; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye index ca8806e3f..ecc1ed278 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye @@ -2,8 +2,10 @@ # Proton Battleye if [ "$_proton_battleye_support" = "true" ] && [ "$_unfrog" != "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 15bf49bf9b938eadd1368922a2d8e2c71824049d HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor acad49573a7846c0199ea3a8f1bd11c42d647ff4 HEAD ); then _patchname='proton_battleye.patch' && _patchmsg="Add support for Proton's Battleye runtime" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 15bf49bf9b938eadd1368922a2d8e2c71824049d HEAD ); then + _patchname='proton_battleye-acad495.patch' && _patchmsg="Add support for Proton's Battleye runtime" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 12d33d21d33788fd46898ea42e9592d33b6e7c8e HEAD ); then _patchname='proton_battleye-15bf49b.patch' && _patchmsg="Add support for Proton's Battleye runtime" && nonuser_patcher fi diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye.patch index 81b3de6fe..d3a430636 100644 --- a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye.patch +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton_battleye/proton_battleye.patch @@ -587,7 +587,7 @@ index 00000000000..f2dc59b07ce + +EXTRADLLFLAGS = -mwindows -municode + -+C_SRCS = \ ++SOURCES = \ + main.c \ diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c new file mode 100644 diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA index e43f0dade..2249ebb5c 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA @@ -1,13 +1,25 @@ #!/bin/bash # IMAGE_FILE_LARGE_ADDRESS_AWARE override - Enable with WINE_LARGE_ADDRESS_AWARE=1 - if [ "$_large_address_aware" = "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 46d0da53b61254d52aa86c6cb524609110cb1213 HEAD ); then + if [ "$_large_address_aware" = "true" ] && [ "$_NOLIB32" != "wow64" ]; then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f9f23e109287b86bf3d1703cad374326c1eb4ccd HEAD ); then if [ "$_use_staging" = "true" ]; then _patchname='LAA-unix-staging.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher else _patchname='LAA-unix.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher fi + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor a81c53504ae32715e6e91bd020fdebd5bef20d48 HEAD ); then + if [ "$_use_staging" = "true" ]; then + _patchname='LAA-unix-staging-f9f23e1.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher + else + _patchname='LAA-unix-f9f23e1.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher + fi + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 46d0da53b61254d52aa86c6cb524609110cb1213 HEAD ); then + if [ "$_use_staging" = "true" ]; then + _patchname='LAA-unix-staging-a81c535.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher + else + _patchname='LAA-unix-a81c535.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher + fi elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ee147d92160cf61cdf808df6fad277198b37a0d4 HEAD ); then if [ "$_use_staging" = "true" ]; then _patchname='LAA-unix-staging-46d0da5.patch' && _patchmsg="Applied large address aware override support (legacy)" && nonuser_patcher diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix-staging.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix-staging.patch index cb0a3b447..392da454e 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix-staging.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix-staging.patch @@ -65,14 +65,11 @@ diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 7236f0acb83..e34abd88093 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c -@@ -1471,8 +1471,8 @@ void server_init_process_done(void) +@@ -1471,6 +1471,7 @@ void server_init_process_done(void) #ifdef __APPLE__ send_server_task_port(); #endif -- if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) -- virtual_set_large_address_space(); -+ if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE -+ || __wine_needs_override_large_address_aware()) virtual_set_large_address_space(); ++ if (__wine_needs_override_large_address_aware()) virtual_set_large_address_space(); /* Install signal handlers; this cannot be done earlier, since we cannot * send exceptions to the debugger before the create process event that @@ -114,25 +111,6 @@ index 1337e2de861..200a777eb5c 100644 -- 2.26.2 -From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 -From: Tk-Glitch -Date: Wed, 1 Sep 2021 15:58:29 +0200 -Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d - - -diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index 75266672f0b..428e13fea1f 100644 ---- a/dlls/ntdll/unix/loader.c -+++ b/dlls/ntdll/unix/loader.c -@@ -358,6 +358,7 @@ - NtWriteFileGather, - NtWriteVirtualMemory, - NtYieldExecution, -+ __wine_needs_override_large_address_aware, - wine_nt_to_unix_file_name, - wine_unix_to_nt_file_name, - }; - From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 From: Torge Matthies Date: Sat, 18 Feb 2023 23:18:53 +0100 @@ -183,14 +161,6 @@ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index ffa38448224..955cc8aec84 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -359,7 +359,6 @@ static void * const syscalls[] = - NtWriteFileGather, - NtWriteVirtualMemory, - NtYieldExecution, -- __wine_needs_override_large_address_aware, - wine_nt_to_unix_file_name, - wine_unix_to_nt_file_name, - }; @@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = load_so_dll, unwind_builtin_dll, @@ -204,13 +174,13 @@ index 44336a43e8c..125e154f31e 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON - extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + extern struct cpu_topology_override *get_cpu_topology_override(void); - extern NTSTATUS unixcall_wine_dbg_write( void *args ) DECLSPEC_HIDDEN; -+extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) DECLSPEC_HIDDEN; - extern NTSTATUS unixcall_wine_server_call( void *args ) DECLSPEC_HIDDEN; - extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ) DECLSPEC_HIDDEN; - extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_dbg_write( void *args ); ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ); + extern NTSTATUS unixcall_wine_server_call( void *args ); + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ); + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 9fb1fc8c688..c10f2468d48 100644 --- a/dlls/ntdll/unix/virtual.c diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix.patch index 35d103a18..b3d00b4bf 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/LAA-unix.patch @@ -65,14 +65,11 @@ diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 7236f0acb83..e34abd88093 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c -@@ -1471,8 +1471,8 @@ void server_init_process_done(void) +@@ -1471,6 +1471,7 @@ void server_init_process_done(void) #ifdef __APPLE__ send_server_task_port(); #endif -- if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) -- virtual_set_large_address_space(); -+ if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE -+ || __wine_needs_override_large_address_aware()) virtual_set_large_address_space(); ++ if (__wine_needs_override_large_address_aware()) virtual_set_large_address_space(); /* Install signal handlers; this cannot be done earlier, since we cannot * send exceptions to the debugger before the create process event that @@ -113,22 +110,103 @@ index 1337e2de861..200a777eb5c 100644 * virtual_set_large_address_space -- 2.26.2 +From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 +From: Torge Matthies +Date: Sat, 18 Feb 2023 23:18:53 +0100 +Subject: [PATCH] Convert LAA syscall to a unix call -From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 -From: Tk-Glitch -Date: Wed, 1 Sep 2021 15:58:29 +0200 -Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d +--- + dlls/ntdll/loader.c | 9 +++++++++ + dlls/ntdll/ntdll.spec | 2 +- + dlls/ntdll/unix/loader.c | 2 +- + dlls/ntdll/unix/virtual.c | 5 +++++ + dlls/ntdll/unixlib.h | 1 + + 5 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 133f6bacd89..6d1a078266f 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -3475,6 +3475,15 @@ NTSTATUS WINAPI __wine_unix_spawnvp( char * const argv[], int wait ) + } ++/*********************************************************************** ++ * __wine_needs_override_large_address_aware ++ */ ++unsigned int CDECL __wine_needs_override_large_address_aware(void) ++{ ++ return WINE_UNIX_CALL( unix_wine_needs_override_large_address_aware, NULL ); ++} ++ ++ + /*********************************************************************** + * wine_server_call + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index e07806f8b0b..5a1bfcee3d7 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1714,7 +1714,7 @@ + @ cdecl -norelay __wine_dbg_strdup(str) + + # Virtual memory +-@ cdecl -syscall __wine_needs_override_large_address_aware() ++@ cdecl __wine_needs_override_large_address_aware() + + # Version + @ cdecl wine_get_version() diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index 75266672f0b..428e13fea1f 100644 +index ffa38448224..955cc8aec84 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -358,6 +358,7 @@ - NtWriteFileGather, - NtWriteVirtualMemory, - NtYieldExecution, -+ __wine_needs_override_large_address_aware, - wine_nt_to_unix_file_name, - wine_unix_to_nt_file_name, - }; +@@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + load_so_dll, + unwind_builtin_dll, + unixcall_wine_dbg_write, ++ unixcall_wine_needs_override_large_address_aware, + unixcall_wine_server_call, + unixcall_wine_server_fd_to_handle, + unixcall_wine_server_handle_to_fd, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 44336a43e8c..125e154f31e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON + extern struct cpu_topology_override *get_cpu_topology_override(void); + + extern NTSTATUS unixcall_wine_dbg_write( void *args ); ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ); + extern NTSTATUS unixcall_wine_server_call( void *args ); + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ); + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 9fb1fc8c688..c10f2468d48 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3508,6 +3508,11 @@ BOOL CDECL __wine_needs_override_large_address_aware(void) + return needs_override; + } + ++NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) ++{ ++ return __wine_needs_override_large_address_aware(); ++} ++ + + /*********************************************************************** + * virtual_map_hypervisor_shared_data +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 22e5663990d..522d9b99978 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -71,6 +71,7 @@ enum ntdll_unix_funcs + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, ++ unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, +-- +2.39.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-a81c535.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-a81c535.patch new file mode 100644 index 000000000..63db5c62e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-a81c535.patch @@ -0,0 +1,243 @@ +From 6a1159fd49718165c61100ea70b0054d5a484ea6 Mon Sep 17 00:00:00 2001 +From: Steven Noonan +Date: Wed, 17 Oct 2018 04:13:37 -0700 +Subject: [PATCH] ntdll/loader: add support for overriding + IMAGE_FILE_LARGE_ADDRESS_AWARE + +Signed-off-by: Steven Noonan +--- + dlls/kernel32/heap.c | 9 ++++++++- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/server.c | 3 ++- + dlls/ntdll/unix/unix_private.h | 2 ++ + dlls/ntdll/unix/virtual.c | 13 +++++++++++++ + 5 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/heap.c b/dlls/kernel32/heap.c +index b7bd6f5f91d..fef060e9c22 100644 +--- a/dlls/kernel32/heap.c ++++ b/dlls/kernel32/heap.c +@@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(globalmem); + + static HANDLE systemHeap; /* globally shared heap */ + ++extern BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + + /*********************************************************************** + * HEAP_CreateSystemHeap +@@ -544,6 +546,10 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + #ifndef _WIN64 + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( GetModuleHandleW(0) ); + #endif ++ static int force_large_address_aware = -1; ++ ++ if (force_large_address_aware == -1) ++ force_large_address_aware = __wine_needs_override_large_address_aware(); + + /* Because GlobalMemoryStatus is identical to GlobalMemoryStatusEX save + for one extra field in the struct, and the lack of a bug, we simply +@@ -584,7 +590,8 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + + /* values are limited to 2Gb unless the app has the IMAGE_FILE_LARGE_ADDRESS_AWARE flag */ + /* page file sizes are not limited (Adobe Illustrator 8 depends on this) */ +- if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) ++ if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) && ++ !force_large_address_aware) + { + if (lpBuffer->dwTotalPhys > MAXLONG) lpBuffer->dwTotalPhys = MAXLONG; + if (lpBuffer->dwAvailPhys > MAXLONG) lpBuffer->dwAvailPhys = MAXLONG; +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index ca427c46c04..10327373959 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1625,6 +1625,9 @@ + @ cdecl -norelay __wine_dbg_output(str) + @ cdecl -norelay __wine_dbg_strdup(str) + ++# Virtual memory ++@ cdecl -syscall __wine_needs_override_large_address_aware() ++ + # Version + @ cdecl wine_get_version() + @ cdecl wine_get_build_id() +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 7236f0acb83..e34abd88093 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1471,8 +1471,8 @@ void server_init_process_done(void) + #ifdef __APPLE__ + send_server_task_port(); + #endif +- if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) +- virtual_set_large_address_space(); ++ if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE ++ || __wine_needs_override_large_address_aware()) virtual_set_large_address_space(); + + /* Install signal handlers; this cannot be done earlier, since we cannot + * send exceptions to the debugger before the create process event that +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index c3ad0a41098..e0326f88a21 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -470,4 +470,6 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n ) + #define towupper(c) ntdll_towupper(c) + #define towlower(c) ntdll_towlower(c) + ++BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + #endif /* __NTDLL_UNIX_PRIVATE_H */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 1337e2de861..200a777eb5c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3420,6 +3420,19 @@ void CDECL virtual_release_address_space(void) + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + } + ++BOOL CDECL __wine_needs_override_large_address_aware(void) ++{ ++ static int needs_override = -1; ++ ++ if (needs_override == -1) ++ { ++ const char *str = getenv( "WINE_LARGE_ADDRESS_AWARE" ); ++ ++ needs_override = !str || atoi(str) == 1; ++ } ++ return needs_override; ++} ++ + + /*********************************************************************** + * virtual_set_large_address_space +-- +2.26.2 + +From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 1 Sep 2021 15:58:29 +0200 +Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d + + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 75266672f0b..428e13fea1f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -358,6 +358,7 @@ + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; + +From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 +From: Torge Matthies +Date: Sat, 18 Feb 2023 23:18:53 +0100 +Subject: [PATCH] Convert LAA syscall to a unix call + +--- + dlls/ntdll/loader.c | 9 +++++++++ + dlls/ntdll/ntdll.spec | 2 +- + dlls/ntdll/unix/loader.c | 2 +- + dlls/ntdll/unix/virtual.c | 5 +++++ + dlls/ntdll/unixlib.h | 1 + + 5 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 133f6bacd89..6d1a078266f 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -3475,6 +3475,15 @@ NTSTATUS WINAPI __wine_unix_spawnvp( char * const argv[], int wait ) + } + + ++/*********************************************************************** ++ * __wine_needs_override_large_address_aware ++ */ ++unsigned int CDECL __wine_needs_override_large_address_aware(void) ++{ ++ return WINE_UNIX_CALL( unix_wine_needs_override_large_address_aware, NULL ); ++} ++ ++ + /*********************************************************************** + * wine_server_call + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index e07806f8b0b..5a1bfcee3d7 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1714,7 +1714,7 @@ + @ cdecl -norelay __wine_dbg_strdup(str) + + # Virtual memory +-@ cdecl -syscall __wine_needs_override_large_address_aware() ++@ cdecl __wine_needs_override_large_address_aware() + + # Version + @ cdecl wine_get_version() +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ffa38448224..955cc8aec84 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -359,7 +359,6 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, +- __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +@@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + load_so_dll, + unwind_builtin_dll, + unixcall_wine_dbg_write, ++ unixcall_wine_needs_override_large_address_aware, + unixcall_wine_server_call, + unixcall_wine_server_fd_to_handle, + unixcall_wine_server_handle_to_fd, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 44336a43e8c..125e154f31e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON + extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + + extern NTSTATUS unixcall_wine_dbg_write( void *args ) DECLSPEC_HIDDEN; ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_call( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 9fb1fc8c688..c10f2468d48 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3508,6 +3508,11 @@ BOOL CDECL __wine_needs_override_large_address_aware(void) + return needs_override; + } + ++NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) ++{ ++ return __wine_needs_override_large_address_aware(); ++} ++ + + /*********************************************************************** + * virtual_map_hypervisor_shared_data +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 22e5663990d..522d9b99978 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -71,6 +71,7 @@ enum ntdll_unix_funcs + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, ++ unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, +-- +2.39.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-f9f23e1.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-f9f23e1.patch new file mode 100644 index 000000000..74a181a2a --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-f9f23e1.patch @@ -0,0 +1,240 @@ +From 6a1159fd49718165c61100ea70b0054d5a484ea6 Mon Sep 17 00:00:00 2001 +From: Steven Noonan +Date: Wed, 17 Oct 2018 04:13:37 -0700 +Subject: [PATCH] ntdll/loader: add support for overriding + IMAGE_FILE_LARGE_ADDRESS_AWARE + +Signed-off-by: Steven Noonan +--- + dlls/kernel32/heap.c | 9 ++++++++- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/server.c | 3 ++- + dlls/ntdll/unix/unix_private.h | 2 ++ + dlls/ntdll/unix/virtual.c | 13 +++++++++++++ + 5 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/heap.c b/dlls/kernel32/heap.c +index b7bd6f5f91d..fef060e9c22 100644 +--- a/dlls/kernel32/heap.c ++++ b/dlls/kernel32/heap.c +@@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(globalmem); + + static HANDLE systemHeap; /* globally shared heap */ + ++extern BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + + /*********************************************************************** + * HEAP_CreateSystemHeap +@@ -544,6 +546,10 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + #ifndef _WIN64 + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( GetModuleHandleW(0) ); + #endif ++ static int force_large_address_aware = -1; ++ ++ if (force_large_address_aware == -1) ++ force_large_address_aware = __wine_needs_override_large_address_aware(); + + /* Because GlobalMemoryStatus is identical to GlobalMemoryStatusEX save + for one extra field in the struct, and the lack of a bug, we simply +@@ -584,7 +590,8 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + + /* values are limited to 2Gb unless the app has the IMAGE_FILE_LARGE_ADDRESS_AWARE flag */ + /* page file sizes are not limited (Adobe Illustrator 8 depends on this) */ +- if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) ++ if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) && ++ !force_large_address_aware) + { + if (lpBuffer->dwTotalPhys > MAXLONG) lpBuffer->dwTotalPhys = MAXLONG; + if (lpBuffer->dwAvailPhys > MAXLONG) lpBuffer->dwAvailPhys = MAXLONG; +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index ca427c46c04..10327373959 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1625,6 +1625,9 @@ + @ cdecl -norelay __wine_dbg_output(str) + @ cdecl -norelay __wine_dbg_strdup(str) + ++# Virtual memory ++@ cdecl -syscall __wine_needs_override_large_address_aware() ++ + # Version + @ cdecl wine_get_version() + @ cdecl wine_get_build_id() +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 7236f0acb83..e34abd88093 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1471,6 +1471,7 @@ void server_init_process_done(void) + #ifdef __APPLE__ + send_server_task_port(); + #endif ++ if (__wine_needs_override_large_address_aware()) virtual_set_large_address_space(); + + /* Install signal handlers; this cannot be done earlier, since we cannot + * send exceptions to the debugger before the create process event that +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index c3ad0a41098..e0326f88a21 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -470,4 +470,6 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n ) + #define towupper(c) ntdll_towupper(c) + #define towlower(c) ntdll_towlower(c) + ++BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + #endif /* __NTDLL_UNIX_PRIVATE_H */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 1337e2de861..200a777eb5c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3420,6 +3420,19 @@ void CDECL virtual_release_address_space(void) + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + } + ++BOOL CDECL __wine_needs_override_large_address_aware(void) ++{ ++ static int needs_override = -1; ++ ++ if (needs_override == -1) ++ { ++ const char *str = getenv( "WINE_LARGE_ADDRESS_AWARE" ); ++ ++ needs_override = !str || atoi(str) == 1; ++ } ++ return needs_override; ++} ++ + + /*********************************************************************** + * virtual_set_large_address_space +-- +2.26.2 + +From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 1 Sep 2021 15:58:29 +0200 +Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d + + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 75266672f0b..428e13fea1f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -358,6 +358,7 @@ + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; + +From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 +From: Torge Matthies +Date: Sat, 18 Feb 2023 23:18:53 +0100 +Subject: [PATCH] Convert LAA syscall to a unix call + +--- + dlls/ntdll/loader.c | 9 +++++++++ + dlls/ntdll/ntdll.spec | 2 +- + dlls/ntdll/unix/loader.c | 2 +- + dlls/ntdll/unix/virtual.c | 5 +++++ + dlls/ntdll/unixlib.h | 1 + + 5 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 133f6bacd89..6d1a078266f 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -3475,6 +3475,15 @@ NTSTATUS WINAPI __wine_unix_spawnvp( char * const argv[], int wait ) + } + + ++/*********************************************************************** ++ * __wine_needs_override_large_address_aware ++ */ ++unsigned int CDECL __wine_needs_override_large_address_aware(void) ++{ ++ return WINE_UNIX_CALL( unix_wine_needs_override_large_address_aware, NULL ); ++} ++ ++ + /*********************************************************************** + * wine_server_call + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index e07806f8b0b..5a1bfcee3d7 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1714,7 +1714,7 @@ + @ cdecl -norelay __wine_dbg_strdup(str) + + # Virtual memory +-@ cdecl -syscall __wine_needs_override_large_address_aware() ++@ cdecl __wine_needs_override_large_address_aware() + + # Version + @ cdecl wine_get_version() +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ffa38448224..955cc8aec84 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -359,7 +359,6 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, +- __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +@@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + load_so_dll, + unwind_builtin_dll, + unixcall_wine_dbg_write, ++ unixcall_wine_needs_override_large_address_aware, + unixcall_wine_server_call, + unixcall_wine_server_fd_to_handle, + unixcall_wine_server_handle_to_fd, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 44336a43e8c..125e154f31e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON + extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + + extern NTSTATUS unixcall_wine_dbg_write( void *args ) DECLSPEC_HIDDEN; ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_call( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 9fb1fc8c688..c10f2468d48 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3508,6 +3508,11 @@ BOOL CDECL __wine_needs_override_large_address_aware(void) + return needs_override; + } + ++NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) ++{ ++ return __wine_needs_override_large_address_aware(); ++} ++ + + /*********************************************************************** + * virtual_map_hypervisor_shared_data +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 22e5663990d..522d9b99978 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -71,6 +71,7 @@ enum ntdll_unix_funcs + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, ++ unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, +-- +2.39.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-a81c535.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-a81c535.patch new file mode 100644 index 000000000..cb0a3b447 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-a81c535.patch @@ -0,0 +1,243 @@ +From 6a1159fd49718165c61100ea70b0054d5a484ea6 Mon Sep 17 00:00:00 2001 +From: Steven Noonan +Date: Wed, 17 Oct 2018 04:13:37 -0700 +Subject: [PATCH] ntdll/loader: add support for overriding + IMAGE_FILE_LARGE_ADDRESS_AWARE + +Signed-off-by: Steven Noonan +--- + dlls/kernel32/heap.c | 9 ++++++++- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/server.c | 3 ++- + dlls/ntdll/unix/unix_private.h | 2 ++ + dlls/ntdll/unix/virtual.c | 13 +++++++++++++ + 5 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/heap.c b/dlls/kernel32/heap.c +index b7bd6f5f91d..fef060e9c22 100644 +--- a/dlls/kernel32/heap.c ++++ b/dlls/kernel32/heap.c +@@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(globalmem); + + static HANDLE systemHeap; /* globally shared heap */ + ++extern BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + + /*********************************************************************** + * HEAP_CreateSystemHeap +@@ -544,6 +546,10 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + #ifndef _WIN64 + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( GetModuleHandleW(0) ); + #endif ++ static int force_large_address_aware = -1; ++ ++ if (force_large_address_aware == -1) ++ force_large_address_aware = __wine_needs_override_large_address_aware(); + + /* Because GlobalMemoryStatus is identical to GlobalMemoryStatusEX save + for one extra field in the struct, and the lack of a bug, we simply +@@ -584,7 +590,8 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + + /* values are limited to 2Gb unless the app has the IMAGE_FILE_LARGE_ADDRESS_AWARE flag */ + /* page file sizes are not limited (Adobe Illustrator 8 depends on this) */ +- if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) ++ if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) && ++ !force_large_address_aware) + { + if (lpBuffer->dwTotalPhys > MAXLONG) lpBuffer->dwTotalPhys = MAXLONG; + if (lpBuffer->dwAvailPhys > MAXLONG) lpBuffer->dwAvailPhys = MAXLONG; +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index ca427c46c04..10327373959 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1625,6 +1625,9 @@ + @ cdecl -norelay __wine_dbg_output(str) + @ cdecl -norelay __wine_dbg_strdup(str) + ++# Virtual memory ++@ cdecl -syscall __wine_needs_override_large_address_aware() ++ + # Version + @ cdecl wine_get_version() + @ cdecl wine_get_build_id() +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 7236f0acb83..e34abd88093 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1471,8 +1471,8 @@ void server_init_process_done(void) + #ifdef __APPLE__ + send_server_task_port(); + #endif +- if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) +- virtual_set_large_address_space(); ++ if (main_image_info.ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE ++ || __wine_needs_override_large_address_aware()) virtual_set_large_address_space(); + + /* Install signal handlers; this cannot be done earlier, since we cannot + * send exceptions to the debugger before the create process event that +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index c3ad0a41098..e0326f88a21 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -470,4 +470,6 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n ) + #define towupper(c) ntdll_towupper(c) + #define towlower(c) ntdll_towlower(c) + ++BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + #endif /* __NTDLL_UNIX_PRIVATE_H */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 1337e2de861..200a777eb5c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3420,6 +3420,19 @@ void CDECL virtual_release_address_space(void) + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + } + ++BOOL CDECL __wine_needs_override_large_address_aware(void) ++{ ++ static int needs_override = -1; ++ ++ if (needs_override == -1) ++ { ++ const char *str = getenv( "WINE_LARGE_ADDRESS_AWARE" ); ++ ++ needs_override = !str || atoi(str) == 1; ++ } ++ return needs_override; ++} ++ + + /*********************************************************************** + * virtual_set_large_address_space +-- +2.26.2 + +From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 1 Sep 2021 15:58:29 +0200 +Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d + + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 75266672f0b..428e13fea1f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -358,6 +358,7 @@ + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; + +From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 +From: Torge Matthies +Date: Sat, 18 Feb 2023 23:18:53 +0100 +Subject: [PATCH] Convert LAA syscall to a unix call + +--- + dlls/ntdll/loader.c | 9 +++++++++ + dlls/ntdll/ntdll.spec | 2 +- + dlls/ntdll/unix/loader.c | 2 +- + dlls/ntdll/unix/virtual.c | 5 +++++ + dlls/ntdll/unixlib.h | 1 + + 5 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 133f6bacd89..6d1a078266f 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -3475,6 +3475,15 @@ NTSTATUS WINAPI __wine_unix_spawnvp( char * const argv[], int wait ) + } + + ++/*********************************************************************** ++ * __wine_needs_override_large_address_aware ++ */ ++unsigned int CDECL __wine_needs_override_large_address_aware(void) ++{ ++ return WINE_UNIX_CALL( unix_wine_needs_override_large_address_aware, NULL ); ++} ++ ++ + /*********************************************************************** + * wine_server_call + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index e07806f8b0b..5a1bfcee3d7 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1714,7 +1714,7 @@ + @ cdecl -norelay __wine_dbg_strdup(str) + + # Virtual memory +-@ cdecl -syscall __wine_needs_override_large_address_aware() ++@ cdecl __wine_needs_override_large_address_aware() + + # Version + @ cdecl wine_get_version() +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ffa38448224..955cc8aec84 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -359,7 +359,6 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, +- __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +@@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + load_so_dll, + unwind_builtin_dll, + unixcall_wine_dbg_write, ++ unixcall_wine_needs_override_large_address_aware, + unixcall_wine_server_call, + unixcall_wine_server_fd_to_handle, + unixcall_wine_server_handle_to_fd, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 44336a43e8c..125e154f31e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON + extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + + extern NTSTATUS unixcall_wine_dbg_write( void *args ) DECLSPEC_HIDDEN; ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_call( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 9fb1fc8c688..c10f2468d48 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3508,6 +3508,11 @@ BOOL CDECL __wine_needs_override_large_address_aware(void) + return needs_override; + } + ++NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) ++{ ++ return __wine_needs_override_large_address_aware(); ++} ++ + + /*********************************************************************** + * virtual_map_hypervisor_shared_data +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 22e5663990d..522d9b99978 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -71,6 +71,7 @@ enum ntdll_unix_funcs + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, ++ unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, +-- +2.39.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-f9f23e1.patch b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-f9f23e1.patch new file mode 100644 index 000000000..4661138ee --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/LAA/legacy/LAA-unix-staging-f9f23e1.patch @@ -0,0 +1,240 @@ +From 6a1159fd49718165c61100ea70b0054d5a484ea6 Mon Sep 17 00:00:00 2001 +From: Steven Noonan +Date: Wed, 17 Oct 2018 04:13:37 -0700 +Subject: [PATCH] ntdll/loader: add support for overriding + IMAGE_FILE_LARGE_ADDRESS_AWARE + +Signed-off-by: Steven Noonan +--- + dlls/kernel32/heap.c | 9 ++++++++- + dlls/ntdll/ntdll.spec | 1 + + dlls/ntdll/unix/server.c | 3 ++- + dlls/ntdll/unix/unix_private.h | 2 ++ + dlls/ntdll/unix/virtual.c | 13 +++++++++++++ + 5 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/dlls/kernel32/heap.c b/dlls/kernel32/heap.c +index b7bd6f5f91d..fef060e9c22 100644 +--- a/dlls/kernel32/heap.c ++++ b/dlls/kernel32/heap.c +@@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(globalmem); + + static HANDLE systemHeap; /* globally shared heap */ + ++extern BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + + /*********************************************************************** + * HEAP_CreateSystemHeap +@@ -544,6 +546,10 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + #ifndef _WIN64 + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( GetModuleHandleW(0) ); + #endif ++ static int force_large_address_aware = -1; ++ ++ if (force_large_address_aware == -1) ++ force_large_address_aware = __wine_needs_override_large_address_aware(); + + /* Because GlobalMemoryStatus is identical to GlobalMemoryStatusEX save + for one extra field in the struct, and the lack of a bug, we simply +@@ -584,7 +590,8 @@ VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) + + /* values are limited to 2Gb unless the app has the IMAGE_FILE_LARGE_ADDRESS_AWARE flag */ + /* page file sizes are not limited (Adobe Illustrator 8 depends on this) */ +- if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) ++ if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) && ++ !force_large_address_aware) + { + if (lpBuffer->dwTotalPhys > MAXLONG) lpBuffer->dwTotalPhys = MAXLONG; + if (lpBuffer->dwAvailPhys > MAXLONG) lpBuffer->dwAvailPhys = MAXLONG; +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index ca427c46c04..10327373959 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1625,6 +1625,9 @@ + @ cdecl -norelay __wine_dbg_output(str) + @ cdecl -norelay __wine_dbg_strdup(str) + ++# Virtual memory ++@ cdecl -syscall __wine_needs_override_large_address_aware() ++ + # Version + @ cdecl wine_get_version() + @ cdecl wine_get_build_id() +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 7236f0acb83..e34abd88093 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -1471,6 +1471,7 @@ void server_init_process_done(void) + #ifdef __APPLE__ + send_server_task_port(); + #endif ++ if (__wine_needs_override_large_address_aware()) virtual_set_large_address_space(); + + /* Install signal handlers; this cannot be done earlier, since we cannot + * send exceptions to the debugger before the create process event that +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index c3ad0a41098..e0326f88a21 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -470,4 +470,6 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n ) + #define towupper(c) ntdll_towupper(c) + #define towlower(c) ntdll_towlower(c) + ++BOOL CDECL __wine_needs_override_large_address_aware(void); ++ + #endif /* __NTDLL_UNIX_PRIVATE_H */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 1337e2de861..200a777eb5c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3420,6 +3420,19 @@ void CDECL virtual_release_address_space(void) + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + } + ++BOOL CDECL __wine_needs_override_large_address_aware(void) ++{ ++ static int needs_override = -1; ++ ++ if (needs_override == -1) ++ { ++ const char *str = getenv( "WINE_LARGE_ADDRESS_AWARE" ); ++ ++ needs_override = !str || atoi(str) == 1; ++ } ++ return needs_override; ++} ++ + + /*********************************************************************** + * virtual_set_large_address_space +-- +2.26.2 + +From 67150fb21e93e2a1d40047355de3c8c7ff2d73ca Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 1 Sep 2021 15:58:29 +0200 +Subject: Add LAA syscall to ntdll loader array following ea6308e364b669adfcb8b1e448c8b08d715bcf6d + + +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 75266672f0b..428e13fea1f 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -358,6 +358,7 @@ + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, ++ __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; + +From 86e5515c1abe1dbedf3d4554e7e6624e613c83e1 Mon Sep 17 00:00:00 2001 +From: Torge Matthies +Date: Sat, 18 Feb 2023 23:18:53 +0100 +Subject: [PATCH] Convert LAA syscall to a unix call + +--- + dlls/ntdll/loader.c | 9 +++++++++ + dlls/ntdll/ntdll.spec | 2 +- + dlls/ntdll/unix/loader.c | 2 +- + dlls/ntdll/unix/virtual.c | 5 +++++ + dlls/ntdll/unixlib.h | 1 + + 5 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 133f6bacd89..6d1a078266f 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -3475,6 +3475,15 @@ NTSTATUS WINAPI __wine_unix_spawnvp( char * const argv[], int wait ) + } + + ++/*********************************************************************** ++ * __wine_needs_override_large_address_aware ++ */ ++unsigned int CDECL __wine_needs_override_large_address_aware(void) ++{ ++ return WINE_UNIX_CALL( unix_wine_needs_override_large_address_aware, NULL ); ++} ++ ++ + /*********************************************************************** + * wine_server_call + */ +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index e07806f8b0b..5a1bfcee3d7 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -1714,7 +1714,7 @@ + @ cdecl -norelay __wine_dbg_strdup(str) + + # Virtual memory +-@ cdecl -syscall __wine_needs_override_large_address_aware() ++@ cdecl __wine_needs_override_large_address_aware() + + # Version + @ cdecl wine_get_version() +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ffa38448224..955cc8aec84 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -359,7 +359,6 @@ static void * const syscalls[] = + NtWriteFileGather, + NtWriteVirtualMemory, + NtYieldExecution, +- __wine_needs_override_large_address_aware, + wine_nt_to_unix_file_name, + wine_unix_to_nt_file_name, + }; +@@ -2104,6 +2103,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = + load_so_dll, + unwind_builtin_dll, + unixcall_wine_dbg_write, ++ unixcall_wine_needs_override_large_address_aware, + unixcall_wine_server_call, + unixcall_wine_server_fd_to_handle, + unixcall_wine_server_handle_to_fd, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 44336a43e8c..125e154f31e 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -322,6 +322,7 @@ extern void set_async_direct_result( HANDLE *async_handle, NTSTATUS status, ULON + extern struct cpu_topology_override *get_cpu_topology_override(void) DECLSPEC_HIDDEN; + + extern NTSTATUS unixcall_wine_dbg_write( void *args ) DECLSPEC_HIDDEN; ++extern NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_call( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_fd_to_handle( void *args ) DECLSPEC_HIDDEN; + extern NTSTATUS unixcall_wine_server_handle_to_fd( void *args ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 9fb1fc8c688..c10f2468d48 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3508,6 +3508,11 @@ BOOL CDECL __wine_needs_override_large_address_aware(void) + return needs_override; + } + ++NTSTATUS unixcall_wine_needs_override_large_address_aware( void *args ) ++{ ++ return __wine_needs_override_large_address_aware(); ++} ++ + + /*********************************************************************** + * virtual_map_hypervisor_shared_data +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 22e5663990d..522d9b99978 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -71,6 +71,7 @@ enum ntdll_unix_funcs + unix_load_so_dll, + unix_unwind_builtin_dll, + unix_wine_dbg_write, ++ unix_wine_needs_override_large_address_aware, + unix_wine_server_call, + unix_wine_server_fd_to_handle, + unix_wine_server_handle_to_fd, +-- +2.39.2 diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/esync b/wine-tkg-git/wine-tkg-patches/proton/esync/esync index 07afed6e1..e693adb73 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/esync/esync +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/esync @@ -2,8 +2,12 @@ # esync if [ "$_use_esync" = "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9f0ae8c992f85f6b67ea484c83ad5c3b28f91a41 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD ); then _patchname='esync-unix-mainline.patch' && _patchmsg="Using Esync (unix, mainline) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor d5f23441ad460844f552d301680e061dd1a5e90a HEAD ); then + _patchname='esync-unix-mainline-c14de4c.patch' && _patchmsg="Using Esync (unix, mainline) patchset" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 9f0ae8c992f85f6b67ea484c83ad5c3b28f91a41 HEAD ); then + _patchname='esync-unix-mainline-d5f2344.patch' && _patchmsg="Using Esync (unix, mainline) patchset" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then _patchname='esync-unix-mainline-9f0ae8c.patch' && _patchmsg="Using Esync (unix, mainline) patchset" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor f076e5f85490a44fd34057df9af1c3ae3e7d5d3b HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/esync-unix-mainline.patch b/wine-tkg-git/wine-tkg-patches/proton/esync/esync-unix-mainline.patch index b88e1c8b1..c84e8d9ad 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/esync/esync-unix-mainline.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/esync-unix-mainline.patch @@ -829,7 +829,7 @@ new file mode 100644 index 00000000000..ed801c71991 --- /dev/null +++ b/dlls/ntdll/unix/esync.c -@@ -0,0 +1,1327 @@ +@@ -0,0 +1,1326 @@ +/* + * eventfd-based synchronization objects + * @@ -877,7 +877,6 @@ index 00000000000..ed801c71991 + +#include "ntstatus.h" +#define WIN32_NO_STATUS -+#define NONAMELESSUNION +#include "windef.h" +#include "winternl.h" +#include "wine/server.h" @@ -2183,37 +2182,37 @@ index 00000000000..188304f3be7 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + -+extern int do_esync(void) DECLSPEC_HIDDEN; -+extern void esync_init(void) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++extern int do_esync(void); ++extern void esync_init(void); ++extern NTSTATUS esync_close( HANDLE handle ); + +extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max); +extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ); + +extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ); +extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_pulse_event( HANDLE handle ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_reset_event( HANDLE handle ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_set_event( HANDLE handle ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_pulse_event( HANDLE handle ); ++extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_reset_event( HANDLE handle ); ++extern NTSTATUS esync_set_event( HANDLE handle ); + +extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ); +extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ); + +extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); +extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ const LARGE_INTEGER *timeout ); + + +/* We have to synchronize on the fd cache mutex so that our calls to receive_fd @@ -2223,7 +2222,7 @@ index 00000000000..188304f3be7 + * "server_fd_mutex" or something similar. */ +extern pthread_mutex_t fd_cache_mutex; + -+extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN; ++extern int receive_fd( obj_handle_t *handle ); diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 33859dabf41..19b73256ef2 100644 --- a/dlls/ntdll/unix/loader.c diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-c14de4c.patch new file mode 100644 index 000000000..e3fe02b30 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-c14de4c.patch @@ -0,0 +1,4734 @@ +From c3e0c63e02d9a305d10b489b39b18adfe2e78393 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:51:21 +0200 +Subject: Esync rebased 5.13+ mainline + + +diff --git a/README.esync b/README.esync +new file mode 100644 +index 00000000000..11d86563a10 +--- /dev/null ++++ b/README.esync +@@ -0,0 +1,196 @@ ++This is eventfd-based synchronization, or 'esync' for short. Turn it on with ++WINEESYNC=1; debug it with +esync. ++ ++== BUGS AND LIMITATIONS == ++ ++Please let me know if you find any bugs. If you can, also attach a log with +++seh,+pid,+esync,+server,+timestamp. ++ ++If you get something like "eventfd: Too many open files" and then things start ++crashing, you've probably run out of file descriptors. esync creates one ++eventfd descriptor for each synchronization object, and some games may use a ++large number of these. Linux by default limits a process to 4096 file ++descriptors, which probably was reasonable back in the nineties but isn't ++really anymore. (Fortunately Debian and derivatives [Ubuntu, Mint] already ++have a reasonable limit.) To raise the limit you'll want to edit ++/etc/security/limits.conf and add a line like ++ ++* hard nofile 1048576 ++ ++then restart your session. ++ ++On distributions using systemd, the settings in `/etc/security/limits.conf` ++will be overridden by systemd's own settings. If you run `ulimit -Hn` and it ++returns a lower number than the one you've previously set, then you can set ++ ++DefaultLimitNOFILE=1048576 ++ ++in both `/etc/systemd/system.conf` and `/etc/systemd/user.conf`. You can then ++execute `sudo systemctl daemon-reexec` and restart your session. Check again ++with `ulimit -Hn` that the limit is correct. ++ ++Also note that if the wineserver has esync active, all clients also must, and ++vice versa. Otherwise things will probably crash quite badly. ++ ++== EXPLANATION == ++ ++The aim is to execute all synchronization operations in "user-space", that is, ++without going through wineserver. We do this using Linux's eventfd ++facility. The main impetus to using eventfd is so that we can poll multiple ++objects at once; in particular we can't do this with futexes, or pthread ++semaphores, or the like. The only way I know of to wait on any of multiple ++objects is to use select/poll/epoll to wait on multiple fds, and eventfd gives ++us those fds in a quite usable way. ++ ++Whenever a semaphore, event, or mutex is created, we have the server, instead ++of creating a traditional server-side event/semaphore/mutex, instead create an ++'esync' primitive. These live in esync.c and are very slim objects; in fact, ++they don't even know what type of primitive they are. The server is involved ++at all because we still need a way of creating named objects, passing handles ++to another process, etc. ++ ++The server creates an eventfd file descriptor with the requested parameters ++and passes it back to ntdll. ntdll creates an object of the appropriate type, ++then caches it in a table. This table is copied almost wholesale from the fd ++cache code in server.c. ++ ++Specific operations follow quite straightforwardly from eventfd: ++ ++* To release an object, or set an event, we simply write() to it. ++* An object is signalled if read() succeeds on it. Notably, we create all ++ eventfd descriptors with O_NONBLOCK, so that we can atomically check if an ++ object is signalled and grab it if it is. This also lets us reset events. ++* For objects whose state should not be reset upon waiting—e.g. manual-reset ++ events—we simply check for the POLLIN flag instead of reading. ++* Semaphores are handled by the EFD_SEMAPHORE flag. This matches up quite well ++ (although with some difficulties; see below). ++* Mutexes store their owner thread locally. This isn't reliable information if ++ a different process's thread owns the mutex, but this doesn't matter—a ++ thread should only care whether it owns the mutex, so it knows whether to ++ try waiting on it or simply to increase the recursion count. ++ ++The interesting part about esync is that (almost) all waits happen in ntdll, ++including those on server-bound objects. The idea here is that on the server ++side, for any waitable object, we create an eventfd file descriptor (not an ++esync primitive), and then pass it to ntdll if the program tries to wait on ++it. These are cached too, so only the first wait will require a round trip to ++the server. Then the server signals the file descriptor as appropriate, and ++thereby wakes up the client. So far this is implemented for processes, ++threads, message queues (difficult; see below), and device managers (necessary ++for drivers to work). All of these are necessarily server-bound, so we ++wouldn't really gain anything by signalling on the client side instead. Of ++course, except possibly for message queues, it's not likely that any program ++(cutting-edge D3D game or not) is going to be causing a great wineserver load ++by waiting on any of these objects; the motivation was rather to provide a way ++to wait on ntdll-bound and server-bound objects at the same time. ++ ++Some cases are still passed to the server, and there's probably no reason not ++to keep them that way. Those that I noticed while testing include: async ++objects, which are internal to the file APIs and never exposed to userspace, ++startup_info objects, which are internal to the loader and signalled when a ++process starts, and keyed events, which are exposed through an ntdll API ++(although not through kernel32) but can't be mixed with other objects (you ++have to use NtWaitForKeyedEvent()). Other cases include: named pipes, debug ++events, sockets, and timers. It's unlikely we'll want to optimize debug events ++or sockets (or any of the other, rather rare, objects), but it is possible ++we'll want to optimize named pipes or timers. ++ ++There were two sort of complications when working out the above. The first one ++was events. The trouble is that (1) the server actually creates some events by ++itself and (2) the server sometimes manipulates events passed by the ++client. Resolving the first case was easy enough, and merely entailed creating ++eventfd descriptors for the events the same way as for processes and threads ++(note that we don't really lose anything this way; the events include ++"LowMemoryCondition" and the event that signals system processes to shut ++down). For the second case I basically had to hook the server-side event ++functions to redirect to esync versions if the event was actually an esync ++primitive. ++ ++The second complication was message queues. The difficulty here is that X11 ++signals events by writing into a pipe (at least I think it's a pipe?), and so ++as a result wineserver has to poll on that descriptor. In theory we could just ++let wineserver do so and then signal us as appropriate, except that wineserver ++only polls on the pipe when the thread is waiting for events (otherwise we'd ++get e.g. keyboard input while the thread is doing something else, and spin ++forever trying to wake up a thread that doesn't care). The obvious solution is ++just to poll on that fd ourselves, and that's what I did—it's just that ++getting the fd from wineserver was kind of ugly, and the code for waiting was ++also kind of ugly basically because we have to wait on both X11's fd and the ++"normal" process/thread-style wineserver fd that we use to signal sent ++messages. The upshot about the whole thing was that races are basically ++impossible, since a thread can only wait on its own queue. ++ ++System APCs already work, since the server will forcibly suspend a thread if ++it's not already waiting, and so we just need to check for EINTR from ++poll(). User APCs and alertable waits are implemented in a similar style to ++message queues (well, sort of): whenever someone executes an alertable wait, ++we add an additional eventfd to the list, which the server signals when an APC ++arrives. If that eventfd gets signaled, we hand it off to the server to take ++care of, and return STATUS_USER_APC. ++ ++Originally I kept the volatile state of semaphores and mutexes inside a ++variable local to the handle, with the knowledge that this would break if ++someone tried to open the handle elsewhere or duplicate it. It did, and so now ++this state is stored inside shared memory. This is of the POSIX variety, is ++allocated by the server (but never mapped there) and lives under the path ++"/wine-esync". ++ ++There are a couple things that this infrastructure can't handle, although ++surprisingly there aren't that many. In particular: ++* Implementing wait-all, i.e. WaitForMultipleObjects(..., TRUE, ...), is not ++ exactly possible the way we'd like it to be possible. In theory that ++ function should wait until it knows all objects are available, then grab ++ them all at once atomically. The server (like the kernel) can do this ++ because the server is single-threaded and can't race with itself. We can't ++ do this in ntdll, though. The approach I've taken I've laid out in great ++ detail in the relevant patch, but for a quick summary we poll on each object ++ until it's signaled (but don't grab it), check them all again, and if ++ they're all signaled we try to grab them all at once in a tight loop, and if ++ we fail on any of them we reset the count on whatever we shouldn't have ++ consumed. Such a blip would necessarily be very quick. ++* The whole patchset only works on Linux, where eventfd is available. However, ++ it should be possible to make it work on a Mac, since eventfd is just a ++ quicker, easier way to use pipes (i.e. instead of writing 1 to the fd you'd ++ write 1 byte; instead of reading a 64-bit value from the fd you'd read as ++ many bytes as you can carry, which is admittedly less than 2**64 but ++ can probably be something reasonable.) It's also possible, although I ++ haven't yet looked, to use some different kind of synchronization ++ primitives, but pipes would be easiest to tack onto this framework. ++* PulseEvent() can't work the way it's supposed to work. Fortunately it's rare ++ and deprecated. It's also explicitly mentioned on MSDN that a thread can ++ miss the notification for a kernel APC, so in a sense we're not necessarily ++ doing anything wrong. ++ ++There are some things that are perfectly implementable but that I just haven't ++done yet: ++* Other synchronizable server primitives. It's unlikely we'll need any of ++ these, except perhaps named pipes (which would honestly be rather difficult) ++ and (maybe) timers. ++* Access masks. We'd need to store these inside ntdll, and validate them when ++ someone tries to execute esync operations. ++ ++This patchset was inspired by Daniel Santos' "hybrid synchronization" ++patchset. My idea was to create a framework whereby even contended waits could ++be executed in userspace, eliminating a lot of the complexity that his ++synchronization primitives used. I do however owe some significant gratitude ++toward him for setting me on the right path. ++ ++I've tried to maximize code separation, both to make any potential rebases ++easier and to ensure that esync is only active when configured. All code in ++existing source files is guarded with "if (do_esync())", and generally that ++condition is followed by "return esync_version_of_this_method(...);", where ++the latter lives in esync.c and is declared in esync.h. I've also tried to ++make the patchset very clear and readable—to write it as if I were going to ++submit it upstream. (Some intermediate patches do break things, which Wine is ++generally against, but I think it's for the better in this case.) I have cut ++some corners, though; there is some error checking missing, or implicit ++assumptions that the program is behaving correctly. ++ ++I've tried to be careful about races. There are a lot of comments whose ++purpose are basically to assure me that races are impossible. In most cases we ++don't have to worry about races since all of the low-level synchronization is ++done by the kernel. ++ ++Anyway, yeah, this is esync. Use it if you like. ++ ++--Zebediah Figura + +diff --git a/configure.ac b/configure.ac +index dd925caf312..f1084515b7a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -507,6 +507,7 @@ AC_CHECK_HEADERS(\ + sys/cdio.h \ + sys/epoll.h \ + sys/event.h \ ++ sys/eventfd.h \ + sys/extattr.h \ + sys/filio.h \ + sys/ipc.h \ +@@ -2260,6 +2261,7 @@ AC_CHECK_FUNCS(\ + port_create \ + posix_fadvise \ + posix_fallocate \ ++ ppoll \ + prctl \ + pread \ + proc_pidinfo \ +@@ -2324,6 +2326,12 @@ case $host_os in + ;; + esac + ++ac_save_LIBS=$LIBS ++AC_SEARCH_LIBS(shm_open, rt, ++ [AC_DEFINE(HAVE_SHM_OPEN, 1, [Define to 1 if you have the `shm_open' function.]) ++ test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")]) ++LIBS=$ac_save_LIBS ++ + AC_CACHE_CHECK([for sched_setaffinity],wine_cv_have_sched_setaffinity, + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[#include ]], [[sched_setaffinity(0, 0, 0);]])],[wine_cv_have_sched_setaffinity=yes],[wine_cv_have_sched_setaffinity=no])) +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 2a47abf342c..0f072a5846e 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -54,6 +54,7 @@ static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK); + + static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG_PTR, SIZE_T *, ULONG, ULONG); + static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG); ++static NTSTATUS (WINAPI *pNtQuerySystemTime)(LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*); + static PSLIST_ENTRY (__fastcall *pRtlInterlockedPushListSList)(PSLIST_HEADER list, PSLIST_ENTRY first, +@@ -177,8 +178,23 @@ static void test_signalandwait(void) + CloseHandle(file); + } + ++static HANDLE mutex, mutex2, mutices[2]; ++ ++static DWORD WINAPI mutex_thread( void *param ) ++{ ++ DWORD expect = (DWORD)(DWORD_PTR)param; ++ DWORD ret; ++ ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == expect, "expected %lu, got %lu\n", expect, ret); ++ ++ if (!ret) ReleaseMutex( mutex ); ++ return 0; ++} ++ + static void test_mutex(void) + { ++ HANDLE thread; + DWORD wait_ret; + BOOL ret; + HANDLE hCreated; +@@ -218,7 +234,8 @@ todo_wine + SetLastError(0xdeadbeef); + hOpened = OpenMutexA(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex"); + ok(hOpened != NULL, "OpenMutex failed with error %ld\n", GetLastError()); +- wait_ret = WaitForSingleObject(hOpened, INFINITE); ++ wait_ret = WaitForSingleObject(hOpened, 0); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: validation is not implemented */ + ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n"); + CloseHandle(hOpened); + +@@ -249,6 +266,7 @@ todo_wine + + SetLastError(0xdeadbeef); + ret = ReleaseMutex(hCreated); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: due to the above */ + ok(!ret && (GetLastError() == ERROR_NOT_OWNER), + "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %ld\n", GetLastError()); + +@@ -287,6 +305,85 @@ todo_wine + CloseHandle(hOpened); + + CloseHandle(hCreated); ++ ++ mutex = CreateMutexA( NULL, FALSE, NULL ); ++ ok(!!mutex, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ } ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ WaitForSingleObject( mutex, 0 ); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)WAIT_TIMEOUT, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ mutex2 = CreateMutexA( NULL, TRUE, NULL ); ++ ok(!!mutex2, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ mutices[0] = mutex; ++ mutices[1] = mutex2; ++ ++ ret = WaitForMultipleObjects( 2, mutices, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForMultipleObjects( 2, mutices, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ + } + + static void test_slist(void) +@@ -462,12 +559,13 @@ static void test_slist(void) + + static void test_event(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + ACL acl; + DWORD ret; + BOOL val; ++ int i; + + /* no sd */ + handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event"); +@@ -571,11 +669,130 @@ static void test_event(void) + ok( ret, "QueryMemoryResourceNotification failed err %lu\n", GetLastError() ); + ok( val == FALSE || val == TRUE, "wrong value %lu\n", val ); + CloseHandle( handle ); ++ ++ handle = CreateEventA( NULL, TRUE, FALSE, NULL ); ++ ok(!!handle, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handle2 = CreateEventA( NULL, FALSE, TRUE, NULL ); ++ ok(!!handle2, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ResetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle2 ); ++ ResetEvent( handle ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ handles[0] = handle2; ++ handles[1] = handle; ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); + } + + static void test_semaphore(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; ++ DWORD ret; ++ LONG prev; ++ int i; + + /* test case sensitivity */ + +@@ -617,6 +834,99 @@ static void test_semaphore(void) + ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError()); + + CloseHandle( handle ); ++ ++ handle = CreateSemaphoreA( NULL, 0, 5, NULL ); ++ ok(!!handle, "CreateSemaphore failed: %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 0, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 1, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 5, &prev ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_TOO_MANY_POSTS, "got error %lu\n", GetLastError()); ++ ok(prev == 1, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 2, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 2, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 4, "got prev %ld\n", prev); ++ ++ for (i = 0; i < 5; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handle2 = CreateSemaphoreA( NULL, 3, 5, NULL ); ++ ok(!!handle2, "CreateSemaphore failed: %lu\n", GetLastError()); ++ ++ ret = ReleaseSemaphore( handle2, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 3, "got prev %ld\n", prev); ++ ++ for (i = 0; i < 4; i++) ++ { ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %lu\n", ret); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %lu\n", ret); + } + + static void test_waitable_timer(void) +@@ -1171,11 +1481,15 @@ static HANDLE modify_handle(HANDLE handle, DWORD modify) + return ULongToHandle(tmp); + } + ++#define TIMEOUT_INFINITE (((LONGLONG)0x7fffffff) << 32 | 0xffffffff) ++ + static void test_WaitForSingleObject(void) + { + HANDLE signaled, nonsignaled, invalid; ++ LARGE_INTEGER ntnow, ntthen; + LARGE_INTEGER timeout; + NTSTATUS status; ++ DWORD now, then; + DWORD ret; + + signaled = CreateEventW(NULL, TRUE, TRUE, NULL); +@@ -1260,6 +1574,68 @@ static void test_WaitForSingleObject(void) + status = pNtWaitForSingleObject(GetCurrentThread(), FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "expected STATUS_TIMEOUT, got %08lx\n", status); + ++ ret = WaitForSingleObject( signaled, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( nonsignaled, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ /* test that a timed wait actually does wait */ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( nonsignaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ok(abs((then - now) - 100) < 5, "got %lu ms\n", then - now); ++ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( signaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == 0, "got %lu\n", ret); ++ ok(abs(then - now) < 5, "got %lu ms\n", then - now); ++ ++ ret = WaitForSingleObject( signaled, INFINITE ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ /* test NT timeouts */ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart + 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = -100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ status = pNtWaitForSingleObject( signaled, FALSE, NULL ); ++ ok(status == 0, "got %#lx\n", status); ++ ++ timeout.QuadPart = TIMEOUT_INFINITE; ++ status = pNtWaitForSingleObject( signaled, FALSE, &timeout ); ++ ok(status == 0, "got %#lx\n", status); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart - 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ + CloseHandle(signaled); + CloseHandle(nonsignaled); + } +@@ -2702,6 +3078,84 @@ static void test_QueueUserAPC(void) + CloseHandle(thread); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %ld\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %ld\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%ld\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %lu\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %ld\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %lu\n", ret); ++ ++ trace("count: %ld\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2728,6 +3182,7 @@ START_TEST(sync) + pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared"); + pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory"); + pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory"); ++ pNtQuerySystemTime = (void *)GetProcAddress(hntdll, "NtQuerySystemTime"); + pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject"); + pNtWaitForMultipleObjects = (void *)GetProcAddress(hntdll, "NtWaitForMultipleObjects"); + pRtlInterlockedPushListSList = (void *)GetProcAddress(hntdll, "RtlInterlockedPushListSList"); +@@ -2763,5 +3218,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index 67847bb9392..c96a62ae006 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -46,6 +46,7 @@ C_SRCS = \ + unix/cdrom.c \ + unix/debug.c \ + unix/env.c \ ++ unix/esync.c \ + unix/file.c \ + unix/loader.c \ + unix/process.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +new file mode 100644 +index 00000000000..ed801c71991 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.c +@@ -0,0 +1,1326 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_POLL_H ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++#include "wine/server.h" ++#include "wine/debug.h" ++ ++#include "unix_private.h" ++#include "esync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(esync); ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("eventfd not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct esync ++{ ++ enum esync_type type; ++ int fd; ++ void *shm; ++}; ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define ESYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct esync)) ++#define ESYNC_LIST_ENTRIES 256 ++ ++static struct esync *esync_list[ESYNC_LIST_ENTRIES]; ++static struct esync esync_list_initial_block[ESYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / ESYNC_LIST_BLOCK_SIZE; ++ return idx % ESYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!esync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) esync_list[0] = esync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ esync_list[entry] = ptr; ++ } ++ } ++ ++ if (!InterlockedCompareExchange( (int *)&esync_list[entry][idx].type, type, 0 )) ++ { ++ esync_list[entry][idx].fd = fd; ++ esync_list[entry][idx].shm = shm; ++ } ++ return &esync_list[entry][idx]; ++} ++ ++static struct esync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES || !esync_list[entry]) return NULL; ++ if (!esync_list[entry][idx].type) return NULL; ++ ++ return &esync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper esync object (i.e. an event, ++ * semaphore, etc. created using create_esync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct esync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ enum esync_type type = 0; ++ unsigned int shm_idx = 0; ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if (!handle) ++ { ++ /* Shadow of the Tomb Raider really likes passing in NULL handles to ++ * various functions. Concerning, but let's avoid a server call. */ ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ if (!(*obj = get_cached_object( handle ))) ++ { ++ SERVER_START_REQ( get_esync_fd ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == handle ); ++ } ++ } ++ SERVER_END_REQ; ++ } ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (*obj) ++ { ++ /* We managed to grab it while in the CS; return it. */ ++ return STATUS_SUCCESS; ++ } ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve fd for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got fd %d for handle %p.\n", fd, handle); ++ ++ *obj = add_to_list( handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ return ret; ++} ++ ++NTSTATUS esync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < ESYNC_LIST_ENTRIES && esync_list[entry]) ++ { ++ if (InterlockedExchange((int *)&esync_list[entry][idx].type, 0)) ++ { ++ close( esync_list[entry][idx].fd ); ++ return STATUS_SUCCESS; ++ } ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_esync( enum esync_type type, HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, int initval, int max ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ /* We have to synchronize on the fd cache CS so that our calls to ++ * receive_fd don't race with theirs. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( create_esync ) ++ { ++ req->access = access; ++ req->initval = initval; ++ req->type = type; ++ req->max = max; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_esync( enum esync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( open_esync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ return ret; ++} ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ uint64_t count64 = count; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (InterlockedCompareExchange( &semaphore->count, count + current, current ) != current); ++ ++ if (prev) *prev = current; ++ ++ /* We don't have to worry about a race between increasing the count and ++ * write(). The fact that we were able to increase the count means that we ++ * have permission to actually write that many releases to the semaphore. */ ++ ++ if (write( obj->fd, &count64, sizeof(count64) ) == -1) ++ return errno_to_status( errno ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_esync( type, handle, access, attr, initial, 0 ); ++} ++ ++NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */ ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Manual-reset events are actually racier than other objects in terms of shm ++ * state. With other objects, races don't matter, because we only treat the shm ++ * state as a hint that lets us skip poll()—we still have to read(). But with ++ * manual-reset events we don't, which means that the shm state can be out of ++ * sync with the actual state. ++ * ++ * In general we shouldn't have to worry about races between modifying the ++ * event and waiting on it. If the state changes while we're waiting, it's ++ * equally plausible that we caught it before or after the state changed. ++ * However, we can have races between SetEvent() and ResetEvent(), so that the ++ * event has inconsistent internal state. ++ * ++ * To solve this we have to use the other field to lock the event. Currently ++ * this is implemented as a spinlock, but I'm not sure if a futex might be ++ * better. I'm also not sure if it's possible to obviate locking by arranging ++ * writes and reads in a certain way. ++ * ++ * Note that we don't have to worry about locking in esync_wait_objects(). ++ * There's only two general patterns: ++ * ++ * WaitFor() SetEvent() ++ * ------------------------- ++ * read() ++ * signaled = 0 ++ * signaled = 1 ++ * write() ++ * ------------------------- ++ * read() ++ * signaled = 1 ++ * signaled = 0 ++ * ++ * ------------------------- ++ * ++ * That is, if SetEvent() tries to signal the event before WaitFor() resets its ++ * signaled state, it won't bother trying to write(), and then the signaled ++ * state will be reset, so the result is a consistent non-signaled event. ++ * There's several variations to this pattern but all of them are protected in ++ * the same way. Note however this is why we have to use interlocked_xchg() ++ * event inside of the lock. ++ */ ++ ++/* Removing this spinlock is harder than it looks. esync_wait_objects() can ++ * deal with inconsistent state well enough, and a race between SetEvent() and ++ * ResetEvent() gives us license to yield either result as long as we act ++ * consistently, but that's not enough. Notably, esync_wait_objects() should ++ * probably act like a fence, so that the second half of esync_set_event() does ++ * not seep past a subsequent reset. That's one problem, but no guarantee there ++ * aren't others. */ ++ ++NTSTATUS esync_set_event( HANDLE handle ) ++{ ++ static const uint64_t value = 1; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling write() if the event wasn't already ++ * signaled. ++ * ++ * For auto-reset events, esync_wait_objects() must grab the kernel object. ++ * Thus if we got into a race so that the shm state is signaled but the ++ * eventfd is unsignaled (i.e. reset shm, set shm, set fd, reset fd), we ++ * *must* signal the fd now, or any waiting threads will never wake up. */ ++ ++ if (!InterlockedExchange( &event->signaled, 1 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ ERR("write: %s\n", strerror(errno)); ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_reset_event( HANDLE handle ) ++{ ++ uint64_t value; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling read() if the event was already signaled. ++ * ++ * For auto-reset events, we have no guarantee that the previous "signaled" ++ * state is actually correct. We need to leave both states unsignaled after ++ * leaving this function, so we always have to read(). */ ++ if (InterlockedExchange( &event->signaled, 0 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (read( obj->fd, &value, sizeof(value) ) == -1 && errno != EWOULDBLOCK && errno != EAGAIN) ++ { ++ ERR("read: %s\n", strerror(errno)); ++ } ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_pulse_event( HANDLE handle ) ++{ ++ uint64_t value = 1; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ read( obj->fd, &value, sizeof(value) ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ struct pollfd fd; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ fd.fd = obj->fd; ++ fd.events = POLLIN; ++ out->EventState = poll( &fd, 1, 0 ); ++ out->EventType = (obj->type == ESYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 ); ++} ++ ++NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ static const uint64_t value = 1; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ /* This is thread-safe, because the only thread that can change the tid to ++ * or from our tid is ours. */ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ mutex->count--; ++ ++ if (!mutex->count) ++ { ++ /* This is also thread-safe, as long as signaling the file is the last ++ * thing we do. Other threads don't care about the tid if it isn't ++ * theirs. */ ++ mutex->tid = 0; ++ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++#define TICKSPERSEC 10000000 ++#define TICKSPERMSEC 10000 ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end ) ++{ ++ int ret; ++ ++ do ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ ++#ifdef HAVE_PPOLL ++ /* We use ppoll() if available since the time granularity is better. */ ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = ppoll( fds, nfds, &tmo_p, NULL ); ++#else ++ ret = poll( fds, nfds, timeleft / TICKSPERMSEC ); ++#endif ++ } ++ else ++ ret = poll( fds, nfds, -1 ); ++ ++ /* If we receive EINTR we were probably suspended (SIGUSR1), possibly for a ++ * system APC. The right thing to do is just try again. */ ++ } while (ret < 0 && errno == EINTR); ++ ++ return ret; ++} ++ ++/* Return TRUE if abandoned. */ ++static BOOL update_grabbed_object( struct esync *obj ) ++{ ++ BOOL ret = FALSE; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we grabbed it means the count is now zero, so nobody else ++ * can (and the only thread that can release it is us). */ ++ if (mutex->tid == ~0) ++ ret = TRUE; ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ } ++ else if (obj->type == ESYNC_SEMAPHORE) ++ { ++ struct semaphore *semaphore = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we were able to grab it at all means the count is nonzero, ++ * and if someone else grabbed it then the count must have been >= 2, ++ * etc. */ ++ InterlockedExchangeAdd( &semaphore->count, -1 ); ++ } ++ else if (obj->type == ESYNC_AUTO_EVENT) ++ { ++ struct event *event = obj->shm; ++ /* We don't have to worry about a race between this and read(), since ++ * this is just a hint, and the real state is in the kernel object. ++ * This might already be 0, but that's okay! */ ++ event->signaled = 0; ++ } ++ ++ return ret; ++} ++ ++/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we ++ * need to delegate to server_select(). */ ++static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero; ++ ++ struct esync *objs[MAXIMUM_WAIT_OBJECTS]; ++ struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1]; ++ int has_esync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD pollcount; ++ ULONGLONG end; ++ int64_t value; ++ ssize_t size; ++ int i, j, ret; ++ ++ /* Grab the APC fd if we don't already have it. */ ++ if (alertable && ntdll_get_thread_data()->esync_apc_fd == -1) ++ { ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( get_esync_apc_fd ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ { ++ fd = receive_fd( &fd_handle ); ++ assert( fd_handle == GetCurrentThreadId() ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ ntdll_get_thread_data()->esync_apc_fd = fd; ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart >= 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_esync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_esync && has_server) ++ FIXME("Can't wait on esync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(esync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) timeleft / TICKSPERSEC, (long) timeleft % TICKSPERSEC); ++ } ++ } ++ ++ if (wait_any || count == 1) ++ { ++ /* Try to check objects now, so we can obviate poll() at least. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (obj) ++ { ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ else if (!mutex->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ if (mutex->tid == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ i += STATUS_ABANDONED_WAIT_0; ++ } ++ else ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ ++ if (semaphore->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ InterlockedDecrement( &semaphore->count ); ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ event->signaled = 0; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ break; ++ } ++ case ESYNC_AUTO_SERVER: ++ case ESYNC_MANUAL_SERVER: ++ case ESYNC_QUEUE: ++ /* We can't wait on any of these. Fortunately I don't think ++ * they'll ever be uncontended anyway (at least, they won't be ++ * performance-critical). */ ++ break; ++ } ++ } ++ ++ fds[i].fd = obj ? obj->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ if (alertable) ++ { ++ fds[i].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[i].events = POLLIN; ++ i++; ++ } ++ pollcount = i; ++ ++ while (1) ++ { ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret > 0) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (alertable) ++ { ++ if (fds[pollcount - 1].revents & POLLIN) ++ goto userapc; ++ } ++ ++ /* Find out which object triggered the wait. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[i].fd, fds[i].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ if (obj) ++ { ++ if (obj->type == ESYNC_MANUAL_EVENT ++ || obj->type == ESYNC_MANUAL_SERVER ++ || obj->type == ESYNC_QUEUE) ++ { ++ /* Don't grab the object, just check if it's signaled. */ ++ if (fds[i].revents & POLLIN) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ } ++ else ++ { ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ /* We found our object. */ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (update_grabbed_object( obj )) ++ return STATUS_ABANDONED_WAIT_0 + i; ++ return i; ++ } ++ } ++ } ++ } ++ ++ /* If we got here, someone else stole (or reset, etc.) whatever ++ * we were waiting for. So keep waiting. */ ++ NtQuerySystemTime( &now ); ++ } ++ else ++ goto err; ++ } ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ while (1) ++ { ++tryagain: ++ /* First step: try to poll on each object in sequence. */ ++ fds[0].events = POLLIN; ++ pollcount = 1; ++ if (alertable) ++ { ++ /* We also need to wait on APCs. */ ++ fds[1].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[1].events = POLLIN; ++ pollcount++; ++ } ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ fds[0].fd = obj ? obj->fd : -1; ++ ++ if (obj && obj->type == ESYNC_MUTEX) ++ { ++ /* It might be ours. */ ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret <= 0) ++ goto err; ++ else if (alertable && (fds[1].revents & POLLIN)) ++ goto userapc; ++ ++ if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[0].fd, fds[0].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ fds[i].fd = objs[i] ? objs[i]->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ /* There's no reason to check for APCs here. */ ++ pollcount = i; ++ ++ /* Poll everything to see if they're still signaled. */ ++ ret = poll( fds, pollcount, 0 ); ++ if (ret == pollcount) ++ { ++ BOOL abandoned = FALSE; ++ ++ /* Quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ if (mutex->tid == GetCurrentThreadId()) ++ break; ++ /* otherwise fall through */ ++ } ++ case ESYNC_SEMAPHORE: ++ case ESYNC_AUTO_EVENT: ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) != sizeof(value)) ++ { ++ /* We were too slow. Put everything back. */ ++ value = 1; ++ for (j = i; j >= 0; j--) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ goto tryagain; /* break out of two loops and a switch */ ++ } ++ break; ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. */ ++ /* Make sure to let ourselves know that we grabbed the mutexes ++ * and semaphores. */ ++ for (i = 0; i < count; i++) ++ abandoned |= update_grabbed_object( objs[i] ); ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ } ++ ++ /* If we got here, ppoll() returned less than all of our objects. ++ * So loop back to the beginning and try again. */ ++ } /* while(1) */ ++ } /* else (wait-all) */ ++ ++err: ++ /* We should only get here if poll() failed. */ ++ ++ if (ret == 0) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else ++ { ++ ERR("ppoll failed: %s\n", strerror(errno)); ++ return errno_to_status( errno ); ++ } ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_select(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* We need to let the server know when we are doing a message wait, and when we ++ * are done with one, so that all of the code surrounding hung queues works. ++ * We also need this for WaitForInputIdle(). */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( esync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from esync_wait_objects(). */ ++NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == ESYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case ESYNC_SEMAPHORE: ++ ret = esync_release_semaphore( signal, 1, NULL ); ++ break; ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ ret = esync_set_event( signal ); ++ break; ++ case ESYNC_MUTEX: ++ ret = esync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return esync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_esync()) ++ { ++ /* make sure the server isn't running with WINEESYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_esync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEESYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open esync shared memory file; make sure no stale wineserver instances are running without WINEESYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} +diff --git a/dlls/ntdll/unix/esync.h b/dlls/ntdll/unix/esync.h +new file mode 100644 +index 00000000000..188304f3be7 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.h +@@ -0,0 +1,61 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void) DECLSPEC_HIDDEN; ++extern void esync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_pulse_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_reset_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_set_event( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ ++ ++/* We have to synchronize on the fd cache mutex so that our calls to receive_fd ++ * don't race with theirs. It looks weird, I know. ++ * ++ * If we weren't trying to avoid touching the code I'd rename the mutex to ++ * "server_fd_mutex" or something similar. */ ++extern pthread_mutex_t fd_cache_mutex; ++ ++extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 33859dabf41..19b73256ef2 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -86,6 +86,7 @@ + #include "winioctl.h" + #include "winternl.h" + #include "unix_private.h" ++#include "esync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1564,6 +1565,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); + syscall_dispatcher = signal_init_syscalls(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index e12ed3a668a..34d4c80eee5 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -94,6 +94,7 @@ + #include "wine/server.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -134,7 +135,7 @@ timeout_t server_start_time = 0; /* time of server startup */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static int initial_cwd = -1; + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -843,7 +844,7 @@ void CDECL wine_server_send_fd( int fd ) + * + * Receive a file descriptor passed from the server. + */ +-static int receive_fd( obj_handle_t *handle ) ++int receive_fd( obj_handle_t *handle ) + { + struct iovec vec; + struct msghdr msghdr; +@@ -1697,6 +1698,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + * retrieve it again */ + fd = remove_fd_from_cache( handle ); + ++ if (do_esync()) ++ esync_close( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index a13e53a..a180ed4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ + #include "wine/exception.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -325,6 +326,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_esync()) ++ return esync_create_semaphore( handle, access, attr, initial, max ); ++ + SERVER_START_REQ( create_semaphore ) + { + req->access = access; +@@ -349,6 +353,10 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + unsigned int ret; + + *handle = 0; ++ ++ if (do_esync()) ++ return esync_open_semaphore( handle, access, attr ); ++ + if ((ret = validate_open_object_attributes( attr ))) return ret; + + SERVER_START_REQ( open_semaphore ) +@@ -385,6 +393,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_semaphore( handle, info, ret_len ); ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -407,6 +418,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_release_semaphore( handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -433,6 +447,10 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; ++ ++ if (do_esync()) ++ return esync_create_event( handle, access, attr, type, state ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_event ) +@@ -461,6 +479,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_event( handle, access, attr ); ++ + SERVER_START_REQ( open_event ) + { + req->access = access; +@@ -481,8 +502,12 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + */ + NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { ++ /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_esync()) ++ return esync_set_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -500,8 +525,12 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + */ + NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { ++ /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_esync()) ++ return esync_reset_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -531,6 +560,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_pulse_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -563,6 +595,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_event( handle, info, ret_len ); ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -589,6 +624,10 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + struct object_attributes *objattr; + + *handle = 0; ++ ++ if (do_esync()) ++ return esync_create_mutex( handle, access, attr, owned ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_mutex ) +@@ -616,6 +655,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_mutex( handle, access, attr ); ++ + SERVER_START_REQ( open_mutex ) + { + req->access = access; +@@ -638,6 +680,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_release_mutex( handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -668,6 +713,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_mutex( handle, info, ret_len ); ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1437,6 +1485,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1462,6 +1517,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_esync()) ++ return esync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 1ff56632496..e7033932bcb 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -58,6 +58,7 @@ struct ntdll_thread_data + { + void *cpu_data[16]; /* reserved for CPU-specific data */ + void *kernel_stack; /* stack for thread startup and kernel syscalls */ ++ int esync_apc_fd; /* fd to wait on for user APCs */ + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index cac8ce4d368..bd89649e769 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2682,6 +2682,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; ++ thread_data->esync_apc_fd = -1; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/dlls/rpcrt4/rpc_server.c b/dlls/rpcrt4/rpc_server.c +index 12260b7298b..a7cad5e273f 100644 +--- a/dlls/rpcrt4/rpc_server.c ++++ b/dlls/rpcrt4/rpc_server.c +@@ -699,10 +699,6 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) + } + LeaveCriticalSection(&cps->cs); + +- EnterCriticalSection(&listen_cs); +- CloseHandle(cps->server_thread); +- cps->server_thread = NULL; +- LeaveCriticalSection(&listen_cs); + TRACE("done\n"); + return 0; + } +@@ -1570,7 +1566,10 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + LIST_FOR_EACH_ENTRY(protseq, &protseqs, RpcServerProtseq, entry) + { + if ((wait_thread = protseq->server_thread)) ++ { ++ protseq->server_thread = NULL; + break; ++ } + } + LeaveCriticalSection(&server_cs); + if (!wait_thread) +@@ -1579,6 +1578,7 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + TRACE("waiting for thread %lu\n", GetThreadId(wait_thread)); + LeaveCriticalSection(&listen_cs); + WaitForSingleObject(wait_thread, INFINITE); ++ CloseHandle(wait_thread); + EnterCriticalSection(&listen_cs); + } + if (listen_done_event == event) +diff --git a/include/config.h.in b/include/config.h.in +index 6ca4e1bc8c8..f315921dee8 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -699,6 +699,9 @@ + /* Define to 1 if you have the `posix_fallocate' function. */ + #undef HAVE_POSIX_FALLOCATE + ++/* Define to 1 if you have the `ppoll' function. */ ++#undef HAVE_PPOLL ++ + /* Define to 1 if you have the `prctl' function. */ + #undef HAVE_PRCTL + +@@ -822,6 +825,9 @@ + /* Define to 1 if `interface_id' is a member of `sg_io_hdr_t'. */ + #undef HAVE_SG_IO_HDR_T_INTERFACE_ID + ++/* Define to 1 if you have the `shm_open' function. */ ++#undef HAVE_SHM_OPEN ++ + /* Define to 1 if `si_fd' is a member of `siginfo_t'. */ + #undef HAVE_SIGINFO_T_SI_FD + +@@ -1022,6 +1028,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EPOLL_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_EVENTFD_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EVENT_H + +diff --git a/server/Makefile.in b/server/Makefile.in +index 9a695cefc30..8bd612b4728 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -11,6 +11,7 @@ C_SRCS = \ + debugger.c \ + device.c \ + directory.c \ ++ esync.c \ + event.c \ + fd.c \ + file.c \ +diff --git a/server/async.c b/server/async.c +index 24fed811da2..1b4f86a1b8b 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -70,6 +70,7 @@ static const struct object_ops async_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -484,6 +485,7 @@ static const struct object_ops iosb_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index b61fa276661..73d858fef82 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -80,6 +80,7 @@ static const struct object_ops atom_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 2cccc5d7a88..85afb0cbdc5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -115,6 +115,7 @@ static const struct object_ops dir_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index e4546832620..54a5fb683cc 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -77,6 +77,7 @@ static const struct object_ops clipboard_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index c9b6995e0b2..ef66260c991 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -64,6 +64,7 @@ static const struct object_ops completion_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index d1e3cae8919..78635f3fae1 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -42,6 +42,7 @@ + #include "wincon.h" + #include "winternl.h" + #include "wine/condrv.h" ++#include "esync.h" + + struct screen_buffer; + +@@ -82,6 +82,7 @@ static const struct object_ops console_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_get_fd, /* get_fd */ +@@ -133,6 +135,7 @@ struct console_server + unsigned int once_input : 1; /* flag if input thread has already been requested */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -142,6 +145,7 @@ static struct fd *console_server_get_fd( struct object *obj ); + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); + + static const struct object_ops console_server_ops = + { +@@ -151,6 +155,7 @@ static const struct object_ops console_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ ++ console_server_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops screen_buffer_ops = + screen_buffer_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -265,6 +271,7 @@ static const struct object_ops console_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops console_input_ops = + console_input_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops console_output_ops = + console_output_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_output_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -526,6 +529,8 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ if (do_esync()) ++ esync_clear( server->esync_fd ); + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -759,6 +767,13 @@ static int console_server_signaled( struct object *obj, struct wait_queue_entry + return !server->console || !list_empty( &server->queue ); + } + ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return server->esync_fd; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -783,6 +798,7 @@ static struct object *create_console_server( void ) + list_init( &server->queue ); + list_init( &server->read_queue ); + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); ++ server->esync_fd = -1; + if (!server->fd) + { + release_object( server ); +@@ -790,6 +806,10 @@ static struct object *create_console_server( void ) + return NULL; + } + allow_fd_caching(server->fd); ++ server->esync_fd = -1; ++ ++ if (do_esync()) ++ server->esync_fd = esync_create_fd( 0, 0 ); + + return &server->obj; + } +@@ -1388,6 +1404,8 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ if (do_esync() && list_empty( &server->queue )) ++ esync_clear( server->esync_fd ); + } + + if (ioctl) +@@ -1486,6 +1504,8 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ if (do_esync() && list_empty( &server->queue )) ++ esync_clear( server->esync_fd ); + + release_object( server ); + } +diff --git a/server/debugger.c b/server/debugger.c +index e4a6c1e43a8..c37f97aa0b6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -73,6 +73,7 @@ static const struct object_ops debug_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +102,7 @@ static const struct object_ops debug_ctx_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index 652da83e1e2..b8ce131c732 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -39,6 +39,7 @@ + #include "handle.h" + #include "request.h" + #include "process.h" ++#include "esync.h" + + /* IRP object */ + +@@ -68,6 +69,7 @@ static const struct object_ops irp_call_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -94,10 +96,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -108,6 +112,7 @@ static const struct object_ops device_manager_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ ++ device_manager_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -152,6 +157,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -203,6 +209,7 @@ static const struct object_ops device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -747,6 +754,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ esync_clear( file->device->manager->esync_fd ); ++ + list_remove( &irp->mgr_entry ); + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } +@@ -782,6 +792,13 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return manager->esync_fd; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -816,6 +833,9 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (do_esync()) ++ close( manager->esync_fd ); + } + + static struct device_manager *create_device_manager(void) +@@ -828,6 +848,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); ++ ++ if (do_esync()) ++ manager->esync_fd = esync_create_fd( 0, 0 ); + } + return manager; + } +@@ -993,6 +1016,9 @@ DECL_HANDLER(get_next_device_request) + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; ++ ++ if (do_esync() && list_empty( &manager->requests )) ++ esync_clear( manager->esync_fd ); + } + else close_handle( current->process, reply->next ); + } +diff --git a/server/directory.c b/server/directory.c +index 9e3fef1177e..007ec1002ac 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -58,6 +58,7 @@ static const struct object_ops object_type_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,6 +98,7 @@ static const struct object_ops directory_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +new file mode 100644 +index 00000000000..5c641fdbe01 +--- /dev/null ++++ b/server/esync.c +@@ -0,0 +1,587 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_SYS_EVENTFD_H ++# include ++#endif ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "file.h" ++#include "esync.h" ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ shm_unlink( shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ fprintf( stderr, "esync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct esync ++{ ++ struct object obj; /* object header */ ++ int fd; /* eventfd file descriptor */ ++ enum esync_type type; ++ unsigned int shm_idx; /* index into the shared memory section */ ++ struct list mutex_entry; /* entry in the mutex list (if applicable) */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ); ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int esync_map_access( struct object *obj, unsigned int access ); ++static void esync_destroy( struct object *obj ); ++ ++const struct object_ops esync_ops = ++{ ++ sizeof(struct esync), /* size */ ++ &no_type, /* type */ ++ esync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ esync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ esync_destroy /* destroy */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ assert( obj->ops == &esync_ops ); ++ fprintf( stderr, "esync fd=%ld\n", esync->fd ); ++} ++ ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ *type = esync->type; ++ return esync->fd; ++} ++ ++static unsigned int esync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void esync_destroy( struct object *obj ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ if (esync->type == ESYNC_MUTEX) ++ list_remove( &esync->mutex_entry ); ++ close( esync->fd ); ++} ++ ++static int type_matches( enum esync_type type1, enum esync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == ESYNC_AUTO_EVENT || type1 == ESYNC_MANUAL_EVENT) && ++ (type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT)); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "esync: couldn't expand shm_addrs array to size %ld\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct esync *create_esync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int initval, int max, enum esync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ struct esync *esync; ++ ++ if ((esync = create_named_object( root, &esync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ int flags = EFD_CLOEXEC | EFD_NONBLOCK; ++ ++ if (type == ESYNC_SEMAPHORE) ++ flags |= EFD_SEMAPHORE; ++ ++ /* initialize it if it didn't already exist */ ++ esync->fd = eventfd( initval, flags ); ++ if (esync->fd == -1) ++ { ++ perror( "eventfd" ); ++ file_set_error(); ++ release_object( esync ); ++ return NULL; ++ } ++ esync->type = type; ++ ++ /* Use the fd as index, since that'll be unique across all ++ * processes, but should hopefully end up also allowing reuse. */ ++ esync->shm_idx = esync->fd + 1; /* we keep index 0 reserved */ ++ while (esync->shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "esync: couldn't expand %s to size %ld: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ switch (type) ++ { ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = get_shm( esync->shm_idx ); ++ semaphore->max = max; ++ semaphore->count = initval; ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = get_shm( esync->shm_idx ); ++ event->signaled = initval ? 1 : 0; ++ event->locked = 0; ++ break; ++ } ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ mutex->tid = initval ? 0 : current->id; ++ mutex->count = initval ? 0 : 1; ++ list_add_tail( &mutex_list, &esync->mutex_entry ); ++ break; ++ } ++ default: ++ assert( 0 ); ++ } ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, esync->type )) ++ { ++ release_object( &esync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ return esync; ++#else ++ /* FIXME: Provide a fallback implementation using pipe(). */ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++/* Create a file descriptor for an existing handle. ++ * Caller must close the handle when it's done; it's not linked to an esync ++ * server object in any way. */ ++int esync_create_fd( int initval, int flags ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ int fd; ++ ++ fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK ); ++ if (fd == -1) ++ perror( "eventfd" ); ++ ++ return fd; ++#else ++ return -1; ++#endif ++} ++ ++/* Wake up a specific fd. */ ++void esync_wake_fd( int fd ) ++{ ++ static const uint64_t value = 1; ++ ++ if (write( fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++} ++ ++/* Wake up a server-side esync object. */ ++void esync_wake_up( struct object *obj ) ++{ ++ enum esync_type dummy; ++ int fd; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &dummy ); ++ esync_wake_fd( fd ); ++ } ++} ++ ++void esync_clear( int fd ) ++{ ++ uint64_t value; ++ ++ /* we don't care about the return value */ ++ read( fd, &value, sizeof(value) ); ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Server-side event support. */ ++void esync_set_event( struct esync *esync ) ++{ ++ static const uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_set_event() fd=%ld\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ { ++ if (write( esync->fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_reset_event( struct esync *esync ) ++{ ++ static uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_reset_event() fd=%ld\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ /* Only bother signaling the fd if we weren't already signaled. */ ++ if (__atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST )) ++ { ++ /* we don't care about the return value */ ++ read( esync->fd, &value, sizeof(value) ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_abandon_mutexes( struct thread *thread ) ++{ ++ struct esync *esync; ++ ++ LIST_FOR_EACH_ENTRY( esync, &mutex_list, struct esync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "esync_abandon_mutexes() fd=%ld\n", esync->fd ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ esync_wake_fd( esync->fd ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_esync) ++{ ++ struct esync *esync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_esync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!req->type) ++ { ++ set_error( STATUS_INVALID_PARAMETER ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->max, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, esync, ++ req->access, objattr->attributes ); ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_esync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &esync_ops, &name, req->attributes ); ++ ++ /* send over the fd */ ++ if (reply->handle) ++ { ++ struct esync *esync; ++ ++ if (!(esync = (struct esync *)get_handle_obj( current->process, reply->handle, ++ 0, &esync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, esync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( esync ); ++ return; ++ } ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++} ++ ++/* Retrieve a file descriptor for an esync object which will be signaled by the ++ * server. The client should only read from (i.e. wait on) this object. */ ++DECL_HANDLER(get_esync_fd) ++{ ++ struct object *obj; ++ enum esync_type type; ++ int fd; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &type ); ++ reply->type = type; ++ if (obj->ops == &esync_ops) ++ { ++ struct esync *esync = (struct esync *)obj; ++ reply->shm_idx = esync->shm_idx; ++ } ++ else ++ reply->shm_idx = 0; ++ send_client_fd( current->process, fd, req->handle ); ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: esync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++/* Return the fd used for waiting on user APCs. */ ++DECL_HANDLER(get_esync_apc_fd) ++{ ++ send_client_fd( current->process, current->esync_apc_fd, current->id ); ++} +diff --git a/server/esync.h b/server/esync.h +new file mode 100644 +index 00000000000..125da8e9d12 +--- /dev/null ++++ b/server/esync.h +@@ -0,0 +1,33 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void); ++void esync_init(void); ++int esync_create_fd( int initval, int flags ); ++void esync_wake_fd( int fd ); ++void esync_wake_up( struct object *obj ); ++void esync_clear( int fd ); ++ ++struct esync; ++ ++extern const struct object_ops esync_ops; ++void esync_set_event( struct esync *esync ); ++void esync_reset_event( struct esync *esync ); ++void esync_abandon_mutexes( struct thread *thread ); +diff --git a/server/event.c b/server/event.c +index 9d8af7c87ea..8607b494b6d 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -35,6 +35,7 @@ + #include "thread.h" + #include "request.h" + #include "security.h" ++#include "esync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -42,13 +43,16 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void event_dump( struct object *obj, int verbose ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -60,6 +64,7 @@ static const struct object_ops event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ ++ event_get_esync_fd, /* get_esync_fd */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -73,7 +78,7 @@ static const struct object_ops event_ops = + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -95,6 +100,7 @@ static const struct object_ops keyed_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -126,6 +132,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ ++ if (do_esync()) ++ event->esync_fd = esync_create_fd( initial_state, 0 ); + } + } + return event; +@@ -133,6 +142,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { ++ struct object *obj; ++ if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + return (struct event *)get_handle_obj( process, handle, access, &event_ops ); + } + +@@ -146,6 +159,12 @@ void pulse_event( struct event *event ) + + void set_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_set_event( (struct esync *)event ); ++ return; ++ } ++ + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); +@@ -153,7 +172,15 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_reset_event( (struct esync *)event ); ++ return; ++ } + event->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( event->esync_fd ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -177,6 +204,13 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ) + return event->signaled; + } + ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = event->manual_reset ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return event->esync_fd; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +@@ -214,6 +248,14 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (do_esync()) ++ close( event->esync_fd ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fd.c b/server/fd.c +index e5a4be7a3df..bbc2462163d 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -102,6 +102,7 @@ + #include "handle.h" + #include "process.h" + #include "request.h" ++#include "esync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -203,6 +204,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -216,6 +218,7 @@ static const struct object_ops fd_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -257,6 +260,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops inode_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -339,6 +344,7 @@ static const struct object_ops file_lock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1589,6 +1595,9 @@ static void fd_destroy( struct object *obj ) + free( fd->unlink_name ); + free( fd->unix_name ); + } ++ ++ if (do_esync()) ++ close( fd->esync_fd ); + } + + /* check if the desired access is possible without violating */ +@@ -1704,12 +1713,16 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1742,11 +1755,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->completion = NULL; + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); ++ ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 0, 0 ); + return fd; + } + +@@ -2172,6 +2189,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); ++ ++ if (do_esync() && !signaled) ++ esync_clear( fd->esync_fd ); + } + + /* check if events are pending and if yes return which one(s) */ +@@ -2203,6 +2223,15 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ int ret = fd->esync_fd; ++ *type = ESYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 23b6de275cd..49c4ba32b42 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -111,6 +111,7 @@ static const struct object_ops file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index bcbdea5b8ce..cae0ac1e395 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -102,6 +102,7 @@ extern void set_fd_signaled( struct fd *fd, int signaled ); + extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/handle.c b/server/handle.c +index c1fb4a490a9..cb5628b7e06 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -123,6 +123,7 @@ static const struct object_ops handle_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 2a3da247313..61b5014c442 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -81,6 +81,7 @@ static const struct object_ops hook_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index fb2a94cc7b8..18fef4b0466 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -78,6 +78,7 @@ static const struct object_ops mailslot_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -136,6 +137,7 @@ static const struct object_ops mail_writer_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -199,6 +201,7 @@ static const struct object_ops mailslot_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -229,6 +232,7 @@ static const struct object_ops mailslot_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 14ba74638ae..3e02cbb3832 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -36,6 +36,7 @@ + #include "thread.h" + #include "request.h" + #include "unicode.h" ++#include "esync.h" + + /* command-line options */ + int debug_level = 0; +@@ -140,6 +141,9 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_esync()) ++ esync_init(); ++ + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); + init_scheduler(); +diff --git a/server/mapping.c b/server/mapping.c +index 1da05412b6a..10def3ca694 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -68,6 +68,7 @@ static const struct object_ops ranges_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -104,6 +105,7 @@ static const struct object_ops shared_map_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +164,7 @@ static const struct object_ops mapping_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index b4f04ad307b..1235ab4731f 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -61,6 +61,7 @@ static const struct object_ops mutex_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index c83e8c17027..e59a5b6c183 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -118,6 +118,7 @@ static const struct object_ops named_pipe_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +163,7 @@ static const struct object_ops pipe_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -205,6 +207,7 @@ static const struct object_ops pipe_client_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -252,6 +255,7 @@ static const struct object_ops named_pipe_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -283,6 +287,7 @@ static const struct object_ops named_pipe_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 43f636b397b..5b6bb9cbfe1 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -68,6 +68,8 @@ struct object_ops + void (*remove_queue)(struct object *,struct wait_queue_entry *); + /* is object signaled? */ + int (*signaled)(struct object *,struct wait_queue_entry *); ++ /* return the esync fd for this object */ ++ int (*get_esync_fd)(struct object *, enum esync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index 1b6ddb1b982..df50955f621 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -49,6 +49,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + /* process object */ + +@@ -68,6 +69,7 @@ static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -78,6 +80,7 @@ static const struct object_ops process_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ ++ process_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -129,6 +132,7 @@ static const struct object_ops startup_info_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -175,6 +179,7 @@ static const struct object_ops job_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -541,6 +546,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + memset( &process->image_info, 0, sizeof(process->image_info) ); ++ process->esync_fd = -1; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -597,6 +603,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_esync()) ++ process->esync_fd = esync_create_fd( 0, 0 ); ++ + set_fd_events( process->msg_fd, POLLIN ); /* start listening to events */ + return process; + +@@ -645,6 +654,7 @@ static void process_destroy( struct object *obj ) + if (process->token) release_object( process->token ); + free( process->dir_cache ); + free( process->image ); ++ if (do_esync()) close( process->esync_fd ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -668,6 +678,13 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry + return !process->running_threads; + } + ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return process->esync_fd; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index 56092e5b1ac..eec69ddbcaf 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -98,6 +98,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + pe_image_info_t image_info; /* main exe image info */ ++ int esync_fd; /* esync file descriptor (signaled on exit) */ + }; + + /* process functions */ +diff --git a/server/protocol.def b/server/protocol.def +index 45f22624d5d..2a8662354f6 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3713,3 +3713,56 @@ struct handle_info + @REQ(resume_process) + obj_handle_t handle; /* process handle */ + @END ++ ++enum esync_type ++{ ++ ESYNC_SEMAPHORE = 1, ++ ESYNC_AUTO_EVENT, ++ ESYNC_MANUAL_EVENT, ++ ESYNC_MUTEX, ++ ESYNC_AUTO_SERVER, ++ ESYNC_MANUAL_SERVER, ++ ESYNC_QUEUE, ++}; ++ ++/* Create a new eventfd-based synchronization object */ ++@REQ(create_esync) ++ unsigned int access; /* wanted access rights */ ++ int initval; /* initial value */ ++ int type; /* type of esync object */ ++ int max; /* maximum count on a semaphore */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* actual type (may be different for events) */ ++ unsigned int shm_idx; ++@END ++ ++@REQ(open_esync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of esync object (above) */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of esync object (above) */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the esync fd for an object. */ ++@REQ(get_esync_fd) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++/* Notify the server that we are doing a message wait or done with one. */ ++@REQ(esync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++/* Retrieve the fd to wait on for user APCs. */ ++@REQ(get_esync_apc_fd) ++@END +diff --git a/server/queue.c b/server/queue.c +index 42b0f4e6bfd..a78748b96ca 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -43,6 +43,7 @@ + #include "process.h" + #include "request.h" + #include "user.h" ++#include "esync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -144,6 +145,8 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ ++ int esync_fd; /* esync file descriptor (signalled on message) */ ++ int esync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -160,6 +163,7 @@ static void msg_queue_dump( struct object *obj, int verbose ); + static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -175,6 +179,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_add_queue, /* add_queue */ + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ ++ msg_queue_get_esync_fd, /* get_esync_fd */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -212,6 +217,7 @@ static const struct object_ops thread_input_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -313,11 +319,16 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->keystate_lock = 0; ++ queue->esync_fd = -1; ++ queue->esync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); ++ ++ if (do_esync()) ++ queue->esync_fd = esync_create_fd( 0, 0 ); + + thread->queue = queue; + } +@@ -497,6 +508,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + if (queue->keystate_lock) unlock_input_keystate( queue->input ); + queue->keystate_lock = 0; + } ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + + /* check whether msg is a keyboard message */ +@@ -955,6 +969,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (do_esync() && queue->esync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -1010,6 +1028,13 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + return ret || is_signaled( queue ); + } + ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = ESYNC_QUEUE; ++ return queue->esync_fd; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2491,6 +2516,9 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -3493,3 +3521,18 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(esync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->esync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index c00022ff63b..996bff5ef6d 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -167,6 +167,7 @@ static const struct object_ops key_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 97bf1a746d2..20b0ec309f3 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -96,6 +96,7 @@ static const struct object_ops master_socket_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a1f796f85b..d7d3a24e48f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -58,6 +58,7 @@ static const struct object_ops semaphore_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index 30fe6e8380f..a50ace9903f 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -92,6 +92,7 @@ static const struct object_ops serial_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index 7c2bf2cc154..b6d6dcfc4b6 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -67,6 +67,7 @@ static const struct object_ops handler_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index 07e1cf3a2ca..c2dfa8fb8ce 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -172,6 +172,7 @@ static const struct object_ops sock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1173,6 +1174,7 @@ static const struct object_ops ifchange_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1393,6 +1395,7 @@ static const struct object_ops socket_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 86ff09cd7e5..07f3c924f25 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -60,6 +60,7 @@ static const struct object_ops symlink_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 0e916e181bc..1a245c58396 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -51,6 +51,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + + /* thread queues */ +@@ -110,6 +111,7 @@ static const struct object_ops thread_apc_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -147,6 +149,7 @@ static const struct object_ops context_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -169,6 +172,7 @@ static const struct object_ops context_ops = + + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -182,6 +186,7 @@ static const struct object_ops thread_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ ++ thread_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -221,6 +226,8 @@ static inline void init_thread_structure( struct thread *thread ) + thread->context = NULL; + thread->teb = 0; + thread->entry_point = 0; ++ thread->esync_fd = -1; ++ thread->esync_apc_fd = -1; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -357,6 +364,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_esync()) ++ { ++ thread->esync_fd = esync_create_fd( 0, 0 ); ++ thread->esync_apc_fd = esync_create_fd( 0, 0 ); ++ } ++ + set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ + add_process_thread( thread->process, thread ); + return thread; +@@ -437,6 +450,9 @@ static void destroy_thread( struct object *obj ) + if (thread->exit_poll) remove_timeout_user( thread->exit_poll ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ ++ if (do_esync()) ++ close( thread->esync_fd ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -461,6 +477,13 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + return mythread->state == TERMINATED && !mythread->exit_poll; + } + ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return thread->esync_fd; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -1045,6 +1068,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_esync()) ++ esync_wake_up( obj ); ++ + LIST_FOR_EACH( ptr, &obj->wait_queue ) + { + struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); +@@ -1129,8 +1155,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (do_esync() && queue == &thread->user_apc) ++ esync_wake_fd( thread->esync_apc_fd ); ++ } ++ + return 1; + } + +@@ -1176,6 +1207,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); + } ++ ++ if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ esync_clear( thread->esync_apc_fd ); ++ + return apc; + } + +@@ -1292,6 +1327,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_esync()) ++ esync_abandon_mutexes( thread ); + wake_up( &thread->obj, 0 ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); +diff --git a/server/thread.h b/server/thread.h +index 78ca4c201b2..0f6108b684a 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -54,6 +54,8 @@ struct thread + struct process *process; + thread_id_t id; /* thread id */ + struct list mutex_list; /* list of currently owned mutexes */ ++ int esync_fd; /* esync file descriptor (signalled on exit) */ ++ int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index 9aba550fd93..dcbc9e2ece5 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -36,6 +36,7 @@ + #include "file.h" + #include "handle.h" + #include "request.h" ++#include "esync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -48,10 +49,12 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -65,6 +68,7 @@ static const struct object_ops timer_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ ++ timer_get_esync_fd, /* get_esync_fd */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +103,10 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->esync_fd = -1; ++ ++ if (do_esync()) ++ timer->esync_fd = esync_create_fd( 0, 0 ); + } + } + return timer; +@@ -172,6 +180,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( timer->esync_fd ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -205,6 +216,13 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ) + return timer->signaled; + } + ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return timer->esync_fd; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 68cfcf234c1..0f128728b0f 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -147,6 +147,7 @@ static const struct object_ops token_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 24059aac0fe..a79fda5ad80 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -108,6 +108,7 @@ static const struct object_ops window_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 883722eff36..1a031248a7c 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -64,6 +64,7 @@ static const struct object_ops winstation_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -89,6 +90,7 @@ static const struct object_ops desktop_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 69afcb164ccf8d3ecd5e94cf79c1e31698e14e5c Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Wed, 2 Feb 2022 17:02:44 -0500 +Subject: [PATCH] esync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/esync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 55c5695964d..4663374653a 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -526,6 +526,9 @@ NTSTATUS esync_set_event( HANDLE handle ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (obj->type == ESYNC_MANUAL_EVENT) + { + /* Acquire the spinlock. */ diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-d5f2344.patch b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-d5f2344.patch new file mode 100644 index 000000000..b88e1c8b1 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-mainline-d5f2344.patch @@ -0,0 +1,4735 @@ +From c3e0c63e02d9a305d10b489b39b18adfe2e78393 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:51:21 +0200 +Subject: Esync rebased 5.13+ mainline + + +diff --git a/README.esync b/README.esync +new file mode 100644 +index 00000000000..11d86563a10 +--- /dev/null ++++ b/README.esync +@@ -0,0 +1,196 @@ ++This is eventfd-based synchronization, or 'esync' for short. Turn it on with ++WINEESYNC=1; debug it with +esync. ++ ++== BUGS AND LIMITATIONS == ++ ++Please let me know if you find any bugs. If you can, also attach a log with +++seh,+pid,+esync,+server,+timestamp. ++ ++If you get something like "eventfd: Too many open files" and then things start ++crashing, you've probably run out of file descriptors. esync creates one ++eventfd descriptor for each synchronization object, and some games may use a ++large number of these. Linux by default limits a process to 4096 file ++descriptors, which probably was reasonable back in the nineties but isn't ++really anymore. (Fortunately Debian and derivatives [Ubuntu, Mint] already ++have a reasonable limit.) To raise the limit you'll want to edit ++/etc/security/limits.conf and add a line like ++ ++* hard nofile 1048576 ++ ++then restart your session. ++ ++On distributions using systemd, the settings in `/etc/security/limits.conf` ++will be overridden by systemd's own settings. If you run `ulimit -Hn` and it ++returns a lower number than the one you've previously set, then you can set ++ ++DefaultLimitNOFILE=1048576 ++ ++in both `/etc/systemd/system.conf` and `/etc/systemd/user.conf`. You can then ++execute `sudo systemctl daemon-reexec` and restart your session. Check again ++with `ulimit -Hn` that the limit is correct. ++ ++Also note that if the wineserver has esync active, all clients also must, and ++vice versa. Otherwise things will probably crash quite badly. ++ ++== EXPLANATION == ++ ++The aim is to execute all synchronization operations in "user-space", that is, ++without going through wineserver. We do this using Linux's eventfd ++facility. The main impetus to using eventfd is so that we can poll multiple ++objects at once; in particular we can't do this with futexes, or pthread ++semaphores, or the like. The only way I know of to wait on any of multiple ++objects is to use select/poll/epoll to wait on multiple fds, and eventfd gives ++us those fds in a quite usable way. ++ ++Whenever a semaphore, event, or mutex is created, we have the server, instead ++of creating a traditional server-side event/semaphore/mutex, instead create an ++'esync' primitive. These live in esync.c and are very slim objects; in fact, ++they don't even know what type of primitive they are. The server is involved ++at all because we still need a way of creating named objects, passing handles ++to another process, etc. ++ ++The server creates an eventfd file descriptor with the requested parameters ++and passes it back to ntdll. ntdll creates an object of the appropriate type, ++then caches it in a table. This table is copied almost wholesale from the fd ++cache code in server.c. ++ ++Specific operations follow quite straightforwardly from eventfd: ++ ++* To release an object, or set an event, we simply write() to it. ++* An object is signalled if read() succeeds on it. Notably, we create all ++ eventfd descriptors with O_NONBLOCK, so that we can atomically check if an ++ object is signalled and grab it if it is. This also lets us reset events. ++* For objects whose state should not be reset upon waiting—e.g. manual-reset ++ events—we simply check for the POLLIN flag instead of reading. ++* Semaphores are handled by the EFD_SEMAPHORE flag. This matches up quite well ++ (although with some difficulties; see below). ++* Mutexes store their owner thread locally. This isn't reliable information if ++ a different process's thread owns the mutex, but this doesn't matter—a ++ thread should only care whether it owns the mutex, so it knows whether to ++ try waiting on it or simply to increase the recursion count. ++ ++The interesting part about esync is that (almost) all waits happen in ntdll, ++including those on server-bound objects. The idea here is that on the server ++side, for any waitable object, we create an eventfd file descriptor (not an ++esync primitive), and then pass it to ntdll if the program tries to wait on ++it. These are cached too, so only the first wait will require a round trip to ++the server. Then the server signals the file descriptor as appropriate, and ++thereby wakes up the client. So far this is implemented for processes, ++threads, message queues (difficult; see below), and device managers (necessary ++for drivers to work). All of these are necessarily server-bound, so we ++wouldn't really gain anything by signalling on the client side instead. Of ++course, except possibly for message queues, it's not likely that any program ++(cutting-edge D3D game or not) is going to be causing a great wineserver load ++by waiting on any of these objects; the motivation was rather to provide a way ++to wait on ntdll-bound and server-bound objects at the same time. ++ ++Some cases are still passed to the server, and there's probably no reason not ++to keep them that way. Those that I noticed while testing include: async ++objects, which are internal to the file APIs and never exposed to userspace, ++startup_info objects, which are internal to the loader and signalled when a ++process starts, and keyed events, which are exposed through an ntdll API ++(although not through kernel32) but can't be mixed with other objects (you ++have to use NtWaitForKeyedEvent()). Other cases include: named pipes, debug ++events, sockets, and timers. It's unlikely we'll want to optimize debug events ++or sockets (or any of the other, rather rare, objects), but it is possible ++we'll want to optimize named pipes or timers. ++ ++There were two sort of complications when working out the above. The first one ++was events. The trouble is that (1) the server actually creates some events by ++itself and (2) the server sometimes manipulates events passed by the ++client. Resolving the first case was easy enough, and merely entailed creating ++eventfd descriptors for the events the same way as for processes and threads ++(note that we don't really lose anything this way; the events include ++"LowMemoryCondition" and the event that signals system processes to shut ++down). For the second case I basically had to hook the server-side event ++functions to redirect to esync versions if the event was actually an esync ++primitive. ++ ++The second complication was message queues. The difficulty here is that X11 ++signals events by writing into a pipe (at least I think it's a pipe?), and so ++as a result wineserver has to poll on that descriptor. In theory we could just ++let wineserver do so and then signal us as appropriate, except that wineserver ++only polls on the pipe when the thread is waiting for events (otherwise we'd ++get e.g. keyboard input while the thread is doing something else, and spin ++forever trying to wake up a thread that doesn't care). The obvious solution is ++just to poll on that fd ourselves, and that's what I did—it's just that ++getting the fd from wineserver was kind of ugly, and the code for waiting was ++also kind of ugly basically because we have to wait on both X11's fd and the ++"normal" process/thread-style wineserver fd that we use to signal sent ++messages. The upshot about the whole thing was that races are basically ++impossible, since a thread can only wait on its own queue. ++ ++System APCs already work, since the server will forcibly suspend a thread if ++it's not already waiting, and so we just need to check for EINTR from ++poll(). User APCs and alertable waits are implemented in a similar style to ++message queues (well, sort of): whenever someone executes an alertable wait, ++we add an additional eventfd to the list, which the server signals when an APC ++arrives. If that eventfd gets signaled, we hand it off to the server to take ++care of, and return STATUS_USER_APC. ++ ++Originally I kept the volatile state of semaphores and mutexes inside a ++variable local to the handle, with the knowledge that this would break if ++someone tried to open the handle elsewhere or duplicate it. It did, and so now ++this state is stored inside shared memory. This is of the POSIX variety, is ++allocated by the server (but never mapped there) and lives under the path ++"/wine-esync". ++ ++There are a couple things that this infrastructure can't handle, although ++surprisingly there aren't that many. In particular: ++* Implementing wait-all, i.e. WaitForMultipleObjects(..., TRUE, ...), is not ++ exactly possible the way we'd like it to be possible. In theory that ++ function should wait until it knows all objects are available, then grab ++ them all at once atomically. The server (like the kernel) can do this ++ because the server is single-threaded and can't race with itself. We can't ++ do this in ntdll, though. The approach I've taken I've laid out in great ++ detail in the relevant patch, but for a quick summary we poll on each object ++ until it's signaled (but don't grab it), check them all again, and if ++ they're all signaled we try to grab them all at once in a tight loop, and if ++ we fail on any of them we reset the count on whatever we shouldn't have ++ consumed. Such a blip would necessarily be very quick. ++* The whole patchset only works on Linux, where eventfd is available. However, ++ it should be possible to make it work on a Mac, since eventfd is just a ++ quicker, easier way to use pipes (i.e. instead of writing 1 to the fd you'd ++ write 1 byte; instead of reading a 64-bit value from the fd you'd read as ++ many bytes as you can carry, which is admittedly less than 2**64 but ++ can probably be something reasonable.) It's also possible, although I ++ haven't yet looked, to use some different kind of synchronization ++ primitives, but pipes would be easiest to tack onto this framework. ++* PulseEvent() can't work the way it's supposed to work. Fortunately it's rare ++ and deprecated. It's also explicitly mentioned on MSDN that a thread can ++ miss the notification for a kernel APC, so in a sense we're not necessarily ++ doing anything wrong. ++ ++There are some things that are perfectly implementable but that I just haven't ++done yet: ++* Other synchronizable server primitives. It's unlikely we'll need any of ++ these, except perhaps named pipes (which would honestly be rather difficult) ++ and (maybe) timers. ++* Access masks. We'd need to store these inside ntdll, and validate them when ++ someone tries to execute esync operations. ++ ++This patchset was inspired by Daniel Santos' "hybrid synchronization" ++patchset. My idea was to create a framework whereby even contended waits could ++be executed in userspace, eliminating a lot of the complexity that his ++synchronization primitives used. I do however owe some significant gratitude ++toward him for setting me on the right path. ++ ++I've tried to maximize code separation, both to make any potential rebases ++easier and to ensure that esync is only active when configured. All code in ++existing source files is guarded with "if (do_esync())", and generally that ++condition is followed by "return esync_version_of_this_method(...);", where ++the latter lives in esync.c and is declared in esync.h. I've also tried to ++make the patchset very clear and readable—to write it as if I were going to ++submit it upstream. (Some intermediate patches do break things, which Wine is ++generally against, but I think it's for the better in this case.) I have cut ++some corners, though; there is some error checking missing, or implicit ++assumptions that the program is behaving correctly. ++ ++I've tried to be careful about races. There are a lot of comments whose ++purpose are basically to assure me that races are impossible. In most cases we ++don't have to worry about races since all of the low-level synchronization is ++done by the kernel. ++ ++Anyway, yeah, this is esync. Use it if you like. ++ ++--Zebediah Figura + +diff --git a/configure.ac b/configure.ac +index dd925caf312..f1084515b7a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -507,6 +507,7 @@ AC_CHECK_HEADERS(\ + sys/cdio.h \ + sys/epoll.h \ + sys/event.h \ ++ sys/eventfd.h \ + sys/extattr.h \ + sys/filio.h \ + sys/ipc.h \ +@@ -2260,6 +2261,7 @@ AC_CHECK_FUNCS(\ + port_create \ + posix_fadvise \ + posix_fallocate \ ++ ppoll \ + prctl \ + pread \ + proc_pidinfo \ +@@ -2324,6 +2326,12 @@ case $host_os in + ;; + esac + ++ac_save_LIBS=$LIBS ++AC_SEARCH_LIBS(shm_open, rt, ++ [AC_DEFINE(HAVE_SHM_OPEN, 1, [Define to 1 if you have the `shm_open' function.]) ++ test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")]) ++LIBS=$ac_save_LIBS ++ + AC_CACHE_CHECK([for sched_setaffinity],wine_cv_have_sched_setaffinity, + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[#include ]], [[sched_setaffinity(0, 0, 0);]])],[wine_cv_have_sched_setaffinity=yes],[wine_cv_have_sched_setaffinity=no])) +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 2a47abf342c..0f072a5846e 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -54,6 +54,7 @@ static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK); + + static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG_PTR, SIZE_T *, ULONG, ULONG); + static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG); ++static NTSTATUS (WINAPI *pNtQuerySystemTime)(LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*); + static PSLIST_ENTRY (__fastcall *pRtlInterlockedPushListSList)(PSLIST_HEADER list, PSLIST_ENTRY first, +@@ -177,8 +178,23 @@ static void test_signalandwait(void) + CloseHandle(file); + } + ++static HANDLE mutex, mutex2, mutices[2]; ++ ++static DWORD WINAPI mutex_thread( void *param ) ++{ ++ DWORD expect = (DWORD)(DWORD_PTR)param; ++ DWORD ret; ++ ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == expect, "expected %lu, got %lu\n", expect, ret); ++ ++ if (!ret) ReleaseMutex( mutex ); ++ return 0; ++} ++ + static void test_mutex(void) + { ++ HANDLE thread; + DWORD wait_ret; + BOOL ret; + HANDLE hCreated; +@@ -218,7 +234,8 @@ todo_wine + SetLastError(0xdeadbeef); + hOpened = OpenMutexA(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex"); + ok(hOpened != NULL, "OpenMutex failed with error %ld\n", GetLastError()); +- wait_ret = WaitForSingleObject(hOpened, INFINITE); ++ wait_ret = WaitForSingleObject(hOpened, 0); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: validation is not implemented */ + ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n"); + CloseHandle(hOpened); + +@@ -249,6 +266,7 @@ todo_wine + + SetLastError(0xdeadbeef); + ret = ReleaseMutex(hCreated); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: due to the above */ + ok(!ret && (GetLastError() == ERROR_NOT_OWNER), + "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %ld\n", GetLastError()); + +@@ -287,6 +305,85 @@ todo_wine + CloseHandle(hOpened); + + CloseHandle(hCreated); ++ ++ mutex = CreateMutexA( NULL, FALSE, NULL ); ++ ok(!!mutex, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ } ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ WaitForSingleObject( mutex, 0 ); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)WAIT_TIMEOUT, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %lu\n", ret); ++ ++ mutex2 = CreateMutexA( NULL, TRUE, NULL ); ++ ok(!!mutex2, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ mutices[0] = mutex; ++ mutices[1] = mutex2; ++ ++ ret = WaitForMultipleObjects( 2, mutices, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForMultipleObjects( 2, mutices, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( mutex ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( mutex2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ + } + + static void test_slist(void) +@@ -462,12 +559,13 @@ static void test_slist(void) + + static void test_event(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + ACL acl; + DWORD ret; + BOOL val; ++ int i; + + /* no sd */ + handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event"); +@@ -571,11 +669,130 @@ static void test_event(void) + ok( ret, "QueryMemoryResourceNotification failed err %lu\n", GetLastError() ); + ok( val == FALSE || val == TRUE, "wrong value %lu\n", val ); + CloseHandle( handle ); ++ ++ handle = CreateEventA( NULL, TRUE, FALSE, NULL ); ++ ok(!!handle, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handle2 = CreateEventA( NULL, FALSE, TRUE, NULL ); ++ ok(!!handle2, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ResetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ SetEvent( handle2 ); ++ ResetEvent( handle ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ handles[0] = handle2; ++ handles[1] = handle; ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %lu\n", GetLastError()); + } + + static void test_semaphore(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; ++ DWORD ret; ++ LONG prev; ++ int i; + + /* test case sensitivity */ + +@@ -617,6 +834,99 @@ static void test_semaphore(void) + ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError()); + + CloseHandle( handle ); ++ ++ handle = CreateSemaphoreA( NULL, 0, 5, NULL ); ++ ok(!!handle, "CreateSemaphore failed: %lu\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 0, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 1, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 5, &prev ); ++ ok(!ret, "got %ld\n", ret); ++ ok(GetLastError() == ERROR_TOO_MANY_POSTS, "got error %lu\n", GetLastError()); ++ ok(prev == 1, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 2, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 2, "got prev %ld\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 4, "got prev %ld\n", prev); ++ ++ for (i = 0; i < 5; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handle2 = CreateSemaphoreA( NULL, 3, 5, NULL ); ++ ok(!!handle2, "CreateSemaphore failed: %lu\n", GetLastError()); ++ ++ ret = ReleaseSemaphore( handle2, 1, &prev ); ++ ok(ret, "got error %lu\n", GetLastError()); ++ ok(prev == 3, "got prev %ld\n", prev); ++ ++ for (i = 0; i < 4; i++) ++ { ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %lu\n", ret); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %lu\n", ret); + } + + static void test_waitable_timer(void) +@@ -1171,11 +1481,15 @@ static HANDLE modify_handle(HANDLE handle, DWORD modify) + return ULongToHandle(tmp); + } + ++#define TIMEOUT_INFINITE (((LONGLONG)0x7fffffff) << 32 | 0xffffffff) ++ + static void test_WaitForSingleObject(void) + { + HANDLE signaled, nonsignaled, invalid; ++ LARGE_INTEGER ntnow, ntthen; + LARGE_INTEGER timeout; + NTSTATUS status; ++ DWORD now, then; + DWORD ret; + + signaled = CreateEventW(NULL, TRUE, TRUE, NULL); +@@ -1260,6 +1574,68 @@ static void test_WaitForSingleObject(void) + status = pNtWaitForSingleObject(GetCurrentThread(), FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "expected STATUS_TIMEOUT, got %08lx\n", status); + ++ ret = WaitForSingleObject( signaled, 0 ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ ret = WaitForSingleObject( nonsignaled, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ++ /* test that a timed wait actually does wait */ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( nonsignaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == WAIT_TIMEOUT, "got %lu\n", ret); ++ ok(abs((then - now) - 100) < 5, "got %lu ms\n", then - now); ++ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( signaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == 0, "got %lu\n", ret); ++ ok(abs(then - now) < 5, "got %lu ms\n", then - now); ++ ++ ret = WaitForSingleObject( signaled, INFINITE ); ++ ok(ret == 0, "got %lu\n", ret); ++ ++ /* test NT timeouts */ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart + 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = -100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ status = pNtWaitForSingleObject( signaled, FALSE, NULL ); ++ ok(status == 0, "got %#lx\n", status); ++ ++ timeout.QuadPart = TIMEOUT_INFINITE; ++ status = pNtWaitForSingleObject( signaled, FALSE, &timeout ); ++ ok(status == 0, "got %#lx\n", status); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart - 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#lx\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ + CloseHandle(signaled); + CloseHandle(nonsignaled); + } +@@ -2702,6 +3078,84 @@ static void test_QueueUserAPC(void) + CloseHandle(thread); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %ld\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %ld\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%ld\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %lu\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %ld\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %lu\n", ret); ++ ++ trace("count: %ld\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2728,6 +3182,7 @@ START_TEST(sync) + pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared"); + pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory"); + pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory"); ++ pNtQuerySystemTime = (void *)GetProcAddress(hntdll, "NtQuerySystemTime"); + pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject"); + pNtWaitForMultipleObjects = (void *)GetProcAddress(hntdll, "NtWaitForMultipleObjects"); + pRtlInterlockedPushListSList = (void *)GetProcAddress(hntdll, "RtlInterlockedPushListSList"); +@@ -2763,5 +3218,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index 67847bb9392..c96a62ae006 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -46,6 +46,7 @@ C_SRCS = \ + unix/cdrom.c \ + unix/debug.c \ + unix/env.c \ ++ unix/esync.c \ + unix/file.c \ + unix/loader.c \ + unix/process.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +new file mode 100644 +index 00000000000..ed801c71991 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.c +@@ -0,0 +1,1327 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_POLL_H ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#define NONAMELESSUNION ++#include "windef.h" ++#include "winternl.h" ++#include "wine/server.h" ++#include "wine/debug.h" ++ ++#include "unix_private.h" ++#include "esync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(esync); ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("eventfd not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct esync ++{ ++ enum esync_type type; ++ int fd; ++ void *shm; ++}; ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define ESYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct esync)) ++#define ESYNC_LIST_ENTRIES 256 ++ ++static struct esync *esync_list[ESYNC_LIST_ENTRIES]; ++static struct esync esync_list_initial_block[ESYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / ESYNC_LIST_BLOCK_SIZE; ++ return idx % ESYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!esync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) esync_list[0] = esync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ esync_list[entry] = ptr; ++ } ++ } ++ ++ if (!InterlockedCompareExchange( (int *)&esync_list[entry][idx].type, type, 0 )) ++ { ++ esync_list[entry][idx].fd = fd; ++ esync_list[entry][idx].shm = shm; ++ } ++ return &esync_list[entry][idx]; ++} ++ ++static struct esync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES || !esync_list[entry]) return NULL; ++ if (!esync_list[entry][idx].type) return NULL; ++ ++ return &esync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper esync object (i.e. an event, ++ * semaphore, etc. created using create_esync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct esync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ enum esync_type type = 0; ++ unsigned int shm_idx = 0; ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if (!handle) ++ { ++ /* Shadow of the Tomb Raider really likes passing in NULL handles to ++ * various functions. Concerning, but let's avoid a server call. */ ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ if (!(*obj = get_cached_object( handle ))) ++ { ++ SERVER_START_REQ( get_esync_fd ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == handle ); ++ } ++ } ++ SERVER_END_REQ; ++ } ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (*obj) ++ { ++ /* We managed to grab it while in the CS; return it. */ ++ return STATUS_SUCCESS; ++ } ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve fd for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got fd %d for handle %p.\n", fd, handle); ++ ++ *obj = add_to_list( handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ return ret; ++} ++ ++NTSTATUS esync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < ESYNC_LIST_ENTRIES && esync_list[entry]) ++ { ++ if (InterlockedExchange((int *)&esync_list[entry][idx].type, 0)) ++ { ++ close( esync_list[entry][idx].fd ); ++ return STATUS_SUCCESS; ++ } ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_esync( enum esync_type type, HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, int initval, int max ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ /* We have to synchronize on the fd cache CS so that our calls to ++ * receive_fd don't race with theirs. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( create_esync ) ++ { ++ req->access = access; ++ req->initval = initval; ++ req->type = type; ++ req->max = max; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_esync( enum esync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( open_esync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ return ret; ++} ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ uint64_t count64 = count; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (InterlockedCompareExchange( &semaphore->count, count + current, current ) != current); ++ ++ if (prev) *prev = current; ++ ++ /* We don't have to worry about a race between increasing the count and ++ * write(). The fact that we were able to increase the count means that we ++ * have permission to actually write that many releases to the semaphore. */ ++ ++ if (write( obj->fd, &count64, sizeof(count64) ) == -1) ++ return errno_to_status( errno ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_esync( type, handle, access, attr, initial, 0 ); ++} ++ ++NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */ ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Manual-reset events are actually racier than other objects in terms of shm ++ * state. With other objects, races don't matter, because we only treat the shm ++ * state as a hint that lets us skip poll()—we still have to read(). But with ++ * manual-reset events we don't, which means that the shm state can be out of ++ * sync with the actual state. ++ * ++ * In general we shouldn't have to worry about races between modifying the ++ * event and waiting on it. If the state changes while we're waiting, it's ++ * equally plausible that we caught it before or after the state changed. ++ * However, we can have races between SetEvent() and ResetEvent(), so that the ++ * event has inconsistent internal state. ++ * ++ * To solve this we have to use the other field to lock the event. Currently ++ * this is implemented as a spinlock, but I'm not sure if a futex might be ++ * better. I'm also not sure if it's possible to obviate locking by arranging ++ * writes and reads in a certain way. ++ * ++ * Note that we don't have to worry about locking in esync_wait_objects(). ++ * There's only two general patterns: ++ * ++ * WaitFor() SetEvent() ++ * ------------------------- ++ * read() ++ * signaled = 0 ++ * signaled = 1 ++ * write() ++ * ------------------------- ++ * read() ++ * signaled = 1 ++ * signaled = 0 ++ * ++ * ------------------------- ++ * ++ * That is, if SetEvent() tries to signal the event before WaitFor() resets its ++ * signaled state, it won't bother trying to write(), and then the signaled ++ * state will be reset, so the result is a consistent non-signaled event. ++ * There's several variations to this pattern but all of them are protected in ++ * the same way. Note however this is why we have to use interlocked_xchg() ++ * event inside of the lock. ++ */ ++ ++/* Removing this spinlock is harder than it looks. esync_wait_objects() can ++ * deal with inconsistent state well enough, and a race between SetEvent() and ++ * ResetEvent() gives us license to yield either result as long as we act ++ * consistently, but that's not enough. Notably, esync_wait_objects() should ++ * probably act like a fence, so that the second half of esync_set_event() does ++ * not seep past a subsequent reset. That's one problem, but no guarantee there ++ * aren't others. */ ++ ++NTSTATUS esync_set_event( HANDLE handle ) ++{ ++ static const uint64_t value = 1; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling write() if the event wasn't already ++ * signaled. ++ * ++ * For auto-reset events, esync_wait_objects() must grab the kernel object. ++ * Thus if we got into a race so that the shm state is signaled but the ++ * eventfd is unsignaled (i.e. reset shm, set shm, set fd, reset fd), we ++ * *must* signal the fd now, or any waiting threads will never wake up. */ ++ ++ if (!InterlockedExchange( &event->signaled, 1 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ ERR("write: %s\n", strerror(errno)); ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_reset_event( HANDLE handle ) ++{ ++ uint64_t value; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling read() if the event was already signaled. ++ * ++ * For auto-reset events, we have no guarantee that the previous "signaled" ++ * state is actually correct. We need to leave both states unsignaled after ++ * leaving this function, so we always have to read(). */ ++ if (InterlockedExchange( &event->signaled, 0 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (read( obj->fd, &value, sizeof(value) ) == -1 && errno != EWOULDBLOCK && errno != EAGAIN) ++ { ++ ERR("read: %s\n", strerror(errno)); ++ } ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_pulse_event( HANDLE handle ) ++{ ++ uint64_t value = 1; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ read( obj->fd, &value, sizeof(value) ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ struct pollfd fd; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ fd.fd = obj->fd; ++ fd.events = POLLIN; ++ out->EventState = poll( &fd, 1, 0 ); ++ out->EventType = (obj->type == ESYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 ); ++} ++ ++NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ static const uint64_t value = 1; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ /* This is thread-safe, because the only thread that can change the tid to ++ * or from our tid is ours. */ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ mutex->count--; ++ ++ if (!mutex->count) ++ { ++ /* This is also thread-safe, as long as signaling the file is the last ++ * thing we do. Other threads don't care about the tid if it isn't ++ * theirs. */ ++ mutex->tid = 0; ++ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++#define TICKSPERSEC 10000000 ++#define TICKSPERMSEC 10000 ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end ) ++{ ++ int ret; ++ ++ do ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ ++#ifdef HAVE_PPOLL ++ /* We use ppoll() if available since the time granularity is better. */ ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = ppoll( fds, nfds, &tmo_p, NULL ); ++#else ++ ret = poll( fds, nfds, timeleft / TICKSPERMSEC ); ++#endif ++ } ++ else ++ ret = poll( fds, nfds, -1 ); ++ ++ /* If we receive EINTR we were probably suspended (SIGUSR1), possibly for a ++ * system APC. The right thing to do is just try again. */ ++ } while (ret < 0 && errno == EINTR); ++ ++ return ret; ++} ++ ++/* Return TRUE if abandoned. */ ++static BOOL update_grabbed_object( struct esync *obj ) ++{ ++ BOOL ret = FALSE; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we grabbed it means the count is now zero, so nobody else ++ * can (and the only thread that can release it is us). */ ++ if (mutex->tid == ~0) ++ ret = TRUE; ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ } ++ else if (obj->type == ESYNC_SEMAPHORE) ++ { ++ struct semaphore *semaphore = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we were able to grab it at all means the count is nonzero, ++ * and if someone else grabbed it then the count must have been >= 2, ++ * etc. */ ++ InterlockedExchangeAdd( &semaphore->count, -1 ); ++ } ++ else if (obj->type == ESYNC_AUTO_EVENT) ++ { ++ struct event *event = obj->shm; ++ /* We don't have to worry about a race between this and read(), since ++ * this is just a hint, and the real state is in the kernel object. ++ * This might already be 0, but that's okay! */ ++ event->signaled = 0; ++ } ++ ++ return ret; ++} ++ ++/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we ++ * need to delegate to server_select(). */ ++static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero; ++ ++ struct esync *objs[MAXIMUM_WAIT_OBJECTS]; ++ struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1]; ++ int has_esync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD pollcount; ++ ULONGLONG end; ++ int64_t value; ++ ssize_t size; ++ int i, j, ret; ++ ++ /* Grab the APC fd if we don't already have it. */ ++ if (alertable && ntdll_get_thread_data()->esync_apc_fd == -1) ++ { ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( get_esync_apc_fd ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ { ++ fd = receive_fd( &fd_handle ); ++ assert( fd_handle == GetCurrentThreadId() ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ ntdll_get_thread_data()->esync_apc_fd = fd; ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart >= 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_esync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_esync && has_server) ++ FIXME("Can't wait on esync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(esync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) timeleft / TICKSPERSEC, (long) timeleft % TICKSPERSEC); ++ } ++ } ++ ++ if (wait_any || count == 1) ++ { ++ /* Try to check objects now, so we can obviate poll() at least. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (obj) ++ { ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ else if (!mutex->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ if (mutex->tid == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ i += STATUS_ABANDONED_WAIT_0; ++ } ++ else ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ ++ if (semaphore->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ InterlockedDecrement( &semaphore->count ); ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ event->signaled = 0; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ break; ++ } ++ case ESYNC_AUTO_SERVER: ++ case ESYNC_MANUAL_SERVER: ++ case ESYNC_QUEUE: ++ /* We can't wait on any of these. Fortunately I don't think ++ * they'll ever be uncontended anyway (at least, they won't be ++ * performance-critical). */ ++ break; ++ } ++ } ++ ++ fds[i].fd = obj ? obj->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ if (alertable) ++ { ++ fds[i].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[i].events = POLLIN; ++ i++; ++ } ++ pollcount = i; ++ ++ while (1) ++ { ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret > 0) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (alertable) ++ { ++ if (fds[pollcount - 1].revents & POLLIN) ++ goto userapc; ++ } ++ ++ /* Find out which object triggered the wait. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[i].fd, fds[i].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ if (obj) ++ { ++ if (obj->type == ESYNC_MANUAL_EVENT ++ || obj->type == ESYNC_MANUAL_SERVER ++ || obj->type == ESYNC_QUEUE) ++ { ++ /* Don't grab the object, just check if it's signaled. */ ++ if (fds[i].revents & POLLIN) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ } ++ else ++ { ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ /* We found our object. */ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (update_grabbed_object( obj )) ++ return STATUS_ABANDONED_WAIT_0 + i; ++ return i; ++ } ++ } ++ } ++ } ++ ++ /* If we got here, someone else stole (or reset, etc.) whatever ++ * we were waiting for. So keep waiting. */ ++ NtQuerySystemTime( &now ); ++ } ++ else ++ goto err; ++ } ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ while (1) ++ { ++tryagain: ++ /* First step: try to poll on each object in sequence. */ ++ fds[0].events = POLLIN; ++ pollcount = 1; ++ if (alertable) ++ { ++ /* We also need to wait on APCs. */ ++ fds[1].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[1].events = POLLIN; ++ pollcount++; ++ } ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ fds[0].fd = obj ? obj->fd : -1; ++ ++ if (obj && obj->type == ESYNC_MUTEX) ++ { ++ /* It might be ours. */ ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret <= 0) ++ goto err; ++ else if (alertable && (fds[1].revents & POLLIN)) ++ goto userapc; ++ ++ if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[0].fd, fds[0].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ fds[i].fd = objs[i] ? objs[i]->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ /* There's no reason to check for APCs here. */ ++ pollcount = i; ++ ++ /* Poll everything to see if they're still signaled. */ ++ ret = poll( fds, pollcount, 0 ); ++ if (ret == pollcount) ++ { ++ BOOL abandoned = FALSE; ++ ++ /* Quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ if (mutex->tid == GetCurrentThreadId()) ++ break; ++ /* otherwise fall through */ ++ } ++ case ESYNC_SEMAPHORE: ++ case ESYNC_AUTO_EVENT: ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) != sizeof(value)) ++ { ++ /* We were too slow. Put everything back. */ ++ value = 1; ++ for (j = i; j >= 0; j--) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ goto tryagain; /* break out of two loops and a switch */ ++ } ++ break; ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. */ ++ /* Make sure to let ourselves know that we grabbed the mutexes ++ * and semaphores. */ ++ for (i = 0; i < count; i++) ++ abandoned |= update_grabbed_object( objs[i] ); ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ } ++ ++ /* If we got here, ppoll() returned less than all of our objects. ++ * So loop back to the beginning and try again. */ ++ } /* while(1) */ ++ } /* else (wait-all) */ ++ ++err: ++ /* We should only get here if poll() failed. */ ++ ++ if (ret == 0) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else ++ { ++ ERR("ppoll failed: %s\n", strerror(errno)); ++ return errno_to_status( errno ); ++ } ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_select(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* We need to let the server know when we are doing a message wait, and when we ++ * are done with one, so that all of the code surrounding hung queues works. ++ * We also need this for WaitForInputIdle(). */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( esync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from esync_wait_objects(). */ ++NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == ESYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case ESYNC_SEMAPHORE: ++ ret = esync_release_semaphore( signal, 1, NULL ); ++ break; ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ ret = esync_set_event( signal ); ++ break; ++ case ESYNC_MUTEX: ++ ret = esync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return esync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_esync()) ++ { ++ /* make sure the server isn't running with WINEESYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_esync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEESYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open esync shared memory file; make sure no stale wineserver instances are running without WINEESYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} +diff --git a/dlls/ntdll/unix/esync.h b/dlls/ntdll/unix/esync.h +new file mode 100644 +index 00000000000..188304f3be7 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.h +@@ -0,0 +1,61 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void) DECLSPEC_HIDDEN; ++extern void esync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_pulse_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_reset_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_set_event( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ ++ ++/* We have to synchronize on the fd cache mutex so that our calls to receive_fd ++ * don't race with theirs. It looks weird, I know. ++ * ++ * If we weren't trying to avoid touching the code I'd rename the mutex to ++ * "server_fd_mutex" or something similar. */ ++extern pthread_mutex_t fd_cache_mutex; ++ ++extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 33859dabf41..19b73256ef2 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -86,6 +86,7 @@ + #include "winioctl.h" + #include "winternl.h" + #include "unix_private.h" ++#include "esync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1564,6 +1565,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); + syscall_dispatcher = signal_init_syscalls(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index e12ed3a668a..34d4c80eee5 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -94,6 +94,7 @@ + #include "wine/server.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -134,7 +135,7 @@ timeout_t server_start_time = 0; /* time of server startup */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static int initial_cwd = -1; + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -843,7 +844,7 @@ void CDECL wine_server_send_fd( int fd ) + * + * Receive a file descriptor passed from the server. + */ +-static int receive_fd( obj_handle_t *handle ) ++int receive_fd( obj_handle_t *handle ) + { + struct iovec vec; + struct msghdr msghdr; +@@ -1697,6 +1698,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + * retrieve it again */ + fd = remove_fd_from_cache( handle ); + ++ if (do_esync()) ++ esync_close( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index a13e53a..a180ed4 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ + #include "wine/exception.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -325,6 +326,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_esync()) ++ return esync_create_semaphore( handle, access, attr, initial, max ); ++ + SERVER_START_REQ( create_semaphore ) + { + req->access = access; +@@ -349,6 +353,10 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + unsigned int ret; + + *handle = 0; ++ ++ if (do_esync()) ++ return esync_open_semaphore( handle, access, attr ); ++ + if ((ret = validate_open_object_attributes( attr ))) return ret; + + SERVER_START_REQ( open_semaphore ) +@@ -385,6 +393,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_semaphore( handle, info, ret_len ); ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -407,6 +418,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_release_semaphore( handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -433,6 +447,10 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; ++ ++ if (do_esync()) ++ return esync_create_event( handle, access, attr, type, state ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_event ) +@@ -461,6 +479,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_event( handle, access, attr ); ++ + SERVER_START_REQ( open_event ) + { + req->access = access; +@@ -481,8 +502,12 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + */ + NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { ++ /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_esync()) ++ return esync_set_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -500,8 +525,12 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + */ + NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { ++ /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_esync()) ++ return esync_reset_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -531,6 +560,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_pulse_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -563,6 +595,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_event( handle, info, ret_len ); ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -589,6 +624,10 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + struct object_attributes *objattr; + + *handle = 0; ++ ++ if (do_esync()) ++ return esync_create_mutex( handle, access, attr, owned ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_mutex ) +@@ -616,6 +655,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_mutex( handle, access, attr ); ++ + SERVER_START_REQ( open_mutex ) + { + req->access = access; +@@ -638,6 +680,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_esync()) ++ return esync_release_mutex( handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -668,6 +713,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_mutex( handle, info, ret_len ); ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1437,6 +1485,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1462,6 +1517,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_esync()) ++ return esync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 1ff56632496..e7033932bcb 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -58,6 +58,7 @@ struct ntdll_thread_data + { + void *cpu_data[16]; /* reserved for CPU-specific data */ + void *kernel_stack; /* stack for thread startup and kernel syscalls */ ++ int esync_apc_fd; /* fd to wait on for user APCs */ + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index cac8ce4d368..bd89649e769 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2682,6 +2682,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; ++ thread_data->esync_apc_fd = -1; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/dlls/rpcrt4/rpc_server.c b/dlls/rpcrt4/rpc_server.c +index 12260b7298b..a7cad5e273f 100644 +--- a/dlls/rpcrt4/rpc_server.c ++++ b/dlls/rpcrt4/rpc_server.c +@@ -699,10 +699,6 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) + } + LeaveCriticalSection(&cps->cs); + +- EnterCriticalSection(&listen_cs); +- CloseHandle(cps->server_thread); +- cps->server_thread = NULL; +- LeaveCriticalSection(&listen_cs); + TRACE("done\n"); + return 0; + } +@@ -1570,7 +1566,10 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + LIST_FOR_EACH_ENTRY(protseq, &protseqs, RpcServerProtseq, entry) + { + if ((wait_thread = protseq->server_thread)) ++ { ++ protseq->server_thread = NULL; + break; ++ } + } + LeaveCriticalSection(&server_cs); + if (!wait_thread) +@@ -1579,6 +1578,7 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + TRACE("waiting for thread %lu\n", GetThreadId(wait_thread)); + LeaveCriticalSection(&listen_cs); + WaitForSingleObject(wait_thread, INFINITE); ++ CloseHandle(wait_thread); + EnterCriticalSection(&listen_cs); + } + if (listen_done_event == event) +diff --git a/include/config.h.in b/include/config.h.in +index 6ca4e1bc8c8..f315921dee8 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -699,6 +699,9 @@ + /* Define to 1 if you have the `posix_fallocate' function. */ + #undef HAVE_POSIX_FALLOCATE + ++/* Define to 1 if you have the `ppoll' function. */ ++#undef HAVE_PPOLL ++ + /* Define to 1 if you have the `prctl' function. */ + #undef HAVE_PRCTL + +@@ -822,6 +825,9 @@ + /* Define to 1 if `interface_id' is a member of `sg_io_hdr_t'. */ + #undef HAVE_SG_IO_HDR_T_INTERFACE_ID + ++/* Define to 1 if you have the `shm_open' function. */ ++#undef HAVE_SHM_OPEN ++ + /* Define to 1 if `si_fd' is a member of `siginfo_t'. */ + #undef HAVE_SIGINFO_T_SI_FD + +@@ -1022,6 +1028,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EPOLL_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_EVENTFD_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EVENT_H + +diff --git a/server/Makefile.in b/server/Makefile.in +index 9a695cefc30..8bd612b4728 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -11,6 +11,7 @@ C_SRCS = \ + debugger.c \ + device.c \ + directory.c \ ++ esync.c \ + event.c \ + fd.c \ + file.c \ +diff --git a/server/async.c b/server/async.c +index 24fed811da2..1b4f86a1b8b 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -70,6 +70,7 @@ static const struct object_ops async_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -484,6 +485,7 @@ static const struct object_ops iosb_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index b61fa276661..73d858fef82 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -80,6 +80,7 @@ static const struct object_ops atom_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 2cccc5d7a88..85afb0cbdc5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -115,6 +115,7 @@ static const struct object_ops dir_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index e4546832620..54a5fb683cc 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -77,6 +77,7 @@ static const struct object_ops clipboard_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index c9b6995e0b2..ef66260c991 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -64,6 +64,7 @@ static const struct object_ops completion_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index d1e3cae8919..78635f3fae1 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -42,6 +42,7 @@ + #include "wincon.h" + #include "winternl.h" + #include "wine/condrv.h" ++#include "esync.h" + + struct screen_buffer; + +@@ -82,6 +82,7 @@ static const struct object_ops console_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_get_fd, /* get_fd */ +@@ -133,6 +135,7 @@ struct console_server + unsigned int once_input : 1; /* flag if input thread has already been requested */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -142,6 +145,7 @@ static struct fd *console_server_get_fd( struct object *obj ); + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); + + static const struct object_ops console_server_ops = + { +@@ -151,6 +155,7 @@ static const struct object_ops console_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ ++ console_server_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops screen_buffer_ops = + screen_buffer_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -265,6 +271,7 @@ static const struct object_ops console_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops console_input_ops = + console_input_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops console_output_ops = + console_output_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_output_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -526,6 +529,8 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ if (do_esync()) ++ esync_clear( server->esync_fd ); + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -759,6 +767,13 @@ static int console_server_signaled( struct object *obj, struct wait_queue_entry + return !server->console || !list_empty( &server->queue ); + } + ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return server->esync_fd; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -783,6 +798,7 @@ static struct object *create_console_server( void ) + list_init( &server->queue ); + list_init( &server->read_queue ); + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); ++ server->esync_fd = -1; + if (!server->fd) + { + release_object( server ); +@@ -790,6 +806,10 @@ static struct object *create_console_server( void ) + return NULL; + } + allow_fd_caching(server->fd); ++ server->esync_fd = -1; ++ ++ if (do_esync()) ++ server->esync_fd = esync_create_fd( 0, 0 ); + + return &server->obj; + } +@@ -1388,6 +1404,8 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ if (do_esync() && list_empty( &server->queue )) ++ esync_clear( server->esync_fd ); + } + + if (ioctl) +@@ -1486,6 +1504,8 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ if (do_esync() && list_empty( &server->queue )) ++ esync_clear( server->esync_fd ); + + release_object( server ); + } +diff --git a/server/debugger.c b/server/debugger.c +index e4a6c1e43a8..c37f97aa0b6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -73,6 +73,7 @@ static const struct object_ops debug_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +102,7 @@ static const struct object_ops debug_ctx_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index 652da83e1e2..b8ce131c732 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -39,6 +39,7 @@ + #include "handle.h" + #include "request.h" + #include "process.h" ++#include "esync.h" + + /* IRP object */ + +@@ -68,6 +69,7 @@ static const struct object_ops irp_call_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -94,10 +96,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -108,6 +112,7 @@ static const struct object_ops device_manager_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ ++ device_manager_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -152,6 +157,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -203,6 +209,7 @@ static const struct object_ops device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -747,6 +754,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ esync_clear( file->device->manager->esync_fd ); ++ + list_remove( &irp->mgr_entry ); + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } +@@ -782,6 +792,13 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return manager->esync_fd; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -816,6 +833,9 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (do_esync()) ++ close( manager->esync_fd ); + } + + static struct device_manager *create_device_manager(void) +@@ -828,6 +848,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); ++ ++ if (do_esync()) ++ manager->esync_fd = esync_create_fd( 0, 0 ); + } + return manager; + } +@@ -993,6 +1016,9 @@ DECL_HANDLER(get_next_device_request) + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; ++ ++ if (do_esync() && list_empty( &manager->requests )) ++ esync_clear( manager->esync_fd ); + } + else close_handle( current->process, reply->next ); + } +diff --git a/server/directory.c b/server/directory.c +index 9e3fef1177e..007ec1002ac 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -58,6 +58,7 @@ static const struct object_ops object_type_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,6 +98,7 @@ static const struct object_ops directory_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +new file mode 100644 +index 00000000000..5c641fdbe01 +--- /dev/null ++++ b/server/esync.c +@@ -0,0 +1,587 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_SYS_EVENTFD_H ++# include ++#endif ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "file.h" ++#include "esync.h" ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ shm_unlink( shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ fprintf( stderr, "esync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct esync ++{ ++ struct object obj; /* object header */ ++ int fd; /* eventfd file descriptor */ ++ enum esync_type type; ++ unsigned int shm_idx; /* index into the shared memory section */ ++ struct list mutex_entry; /* entry in the mutex list (if applicable) */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ); ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int esync_map_access( struct object *obj, unsigned int access ); ++static void esync_destroy( struct object *obj ); ++ ++const struct object_ops esync_ops = ++{ ++ sizeof(struct esync), /* size */ ++ &no_type, /* type */ ++ esync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ esync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ esync_destroy /* destroy */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ assert( obj->ops == &esync_ops ); ++ fprintf( stderr, "esync fd=%ld\n", esync->fd ); ++} ++ ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ *type = esync->type; ++ return esync->fd; ++} ++ ++static unsigned int esync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void esync_destroy( struct object *obj ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ if (esync->type == ESYNC_MUTEX) ++ list_remove( &esync->mutex_entry ); ++ close( esync->fd ); ++} ++ ++static int type_matches( enum esync_type type1, enum esync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == ESYNC_AUTO_EVENT || type1 == ESYNC_MANUAL_EVENT) && ++ (type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT)); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "esync: couldn't expand shm_addrs array to size %ld\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct esync *create_esync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int initval, int max, enum esync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ struct esync *esync; ++ ++ if ((esync = create_named_object( root, &esync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ int flags = EFD_CLOEXEC | EFD_NONBLOCK; ++ ++ if (type == ESYNC_SEMAPHORE) ++ flags |= EFD_SEMAPHORE; ++ ++ /* initialize it if it didn't already exist */ ++ esync->fd = eventfd( initval, flags ); ++ if (esync->fd == -1) ++ { ++ perror( "eventfd" ); ++ file_set_error(); ++ release_object( esync ); ++ return NULL; ++ } ++ esync->type = type; ++ ++ /* Use the fd as index, since that'll be unique across all ++ * processes, but should hopefully end up also allowing reuse. */ ++ esync->shm_idx = esync->fd + 1; /* we keep index 0 reserved */ ++ while (esync->shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "esync: couldn't expand %s to size %ld: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ switch (type) ++ { ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = get_shm( esync->shm_idx ); ++ semaphore->max = max; ++ semaphore->count = initval; ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = get_shm( esync->shm_idx ); ++ event->signaled = initval ? 1 : 0; ++ event->locked = 0; ++ break; ++ } ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ mutex->tid = initval ? 0 : current->id; ++ mutex->count = initval ? 0 : 1; ++ list_add_tail( &mutex_list, &esync->mutex_entry ); ++ break; ++ } ++ default: ++ assert( 0 ); ++ } ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, esync->type )) ++ { ++ release_object( &esync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ return esync; ++#else ++ /* FIXME: Provide a fallback implementation using pipe(). */ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++/* Create a file descriptor for an existing handle. ++ * Caller must close the handle when it's done; it's not linked to an esync ++ * server object in any way. */ ++int esync_create_fd( int initval, int flags ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ int fd; ++ ++ fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK ); ++ if (fd == -1) ++ perror( "eventfd" ); ++ ++ return fd; ++#else ++ return -1; ++#endif ++} ++ ++/* Wake up a specific fd. */ ++void esync_wake_fd( int fd ) ++{ ++ static const uint64_t value = 1; ++ ++ if (write( fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++} ++ ++/* Wake up a server-side esync object. */ ++void esync_wake_up( struct object *obj ) ++{ ++ enum esync_type dummy; ++ int fd; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &dummy ); ++ esync_wake_fd( fd ); ++ } ++} ++ ++void esync_clear( int fd ) ++{ ++ uint64_t value; ++ ++ /* we don't care about the return value */ ++ read( fd, &value, sizeof(value) ); ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Server-side event support. */ ++void esync_set_event( struct esync *esync ) ++{ ++ static const uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_set_event() fd=%ld\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ { ++ if (write( esync->fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_reset_event( struct esync *esync ) ++{ ++ static uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_reset_event() fd=%ld\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ /* Only bother signaling the fd if we weren't already signaled. */ ++ if (__atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST )) ++ { ++ /* we don't care about the return value */ ++ read( esync->fd, &value, sizeof(value) ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_abandon_mutexes( struct thread *thread ) ++{ ++ struct esync *esync; ++ ++ LIST_FOR_EACH_ENTRY( esync, &mutex_list, struct esync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "esync_abandon_mutexes() fd=%ld\n", esync->fd ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ esync_wake_fd( esync->fd ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_esync) ++{ ++ struct esync *esync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_esync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!req->type) ++ { ++ set_error( STATUS_INVALID_PARAMETER ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->max, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, esync, ++ req->access, objattr->attributes ); ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_esync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &esync_ops, &name, req->attributes ); ++ ++ /* send over the fd */ ++ if (reply->handle) ++ { ++ struct esync *esync; ++ ++ if (!(esync = (struct esync *)get_handle_obj( current->process, reply->handle, ++ 0, &esync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, esync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( esync ); ++ return; ++ } ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++} ++ ++/* Retrieve a file descriptor for an esync object which will be signaled by the ++ * server. The client should only read from (i.e. wait on) this object. */ ++DECL_HANDLER(get_esync_fd) ++{ ++ struct object *obj; ++ enum esync_type type; ++ int fd; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &type ); ++ reply->type = type; ++ if (obj->ops == &esync_ops) ++ { ++ struct esync *esync = (struct esync *)obj; ++ reply->shm_idx = esync->shm_idx; ++ } ++ else ++ reply->shm_idx = 0; ++ send_client_fd( current->process, fd, req->handle ); ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: esync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++/* Return the fd used for waiting on user APCs. */ ++DECL_HANDLER(get_esync_apc_fd) ++{ ++ send_client_fd( current->process, current->esync_apc_fd, current->id ); ++} +diff --git a/server/esync.h b/server/esync.h +new file mode 100644 +index 00000000000..125da8e9d12 +--- /dev/null ++++ b/server/esync.h +@@ -0,0 +1,33 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void); ++void esync_init(void); ++int esync_create_fd( int initval, int flags ); ++void esync_wake_fd( int fd ); ++void esync_wake_up( struct object *obj ); ++void esync_clear( int fd ); ++ ++struct esync; ++ ++extern const struct object_ops esync_ops; ++void esync_set_event( struct esync *esync ); ++void esync_reset_event( struct esync *esync ); ++void esync_abandon_mutexes( struct thread *thread ); +diff --git a/server/event.c b/server/event.c +index 9d8af7c87ea..8607b494b6d 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -35,6 +35,7 @@ + #include "thread.h" + #include "request.h" + #include "security.h" ++#include "esync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -42,13 +43,16 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void event_dump( struct object *obj, int verbose ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -60,6 +64,7 @@ static const struct object_ops event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ ++ event_get_esync_fd, /* get_esync_fd */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -73,7 +78,7 @@ static const struct object_ops event_ops = + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -95,6 +100,7 @@ static const struct object_ops keyed_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -126,6 +132,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ ++ if (do_esync()) ++ event->esync_fd = esync_create_fd( initial_state, 0 ); + } + } + return event; +@@ -133,6 +142,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { ++ struct object *obj; ++ if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + return (struct event *)get_handle_obj( process, handle, access, &event_ops ); + } + +@@ -146,6 +159,12 @@ void pulse_event( struct event *event ) + + void set_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_set_event( (struct esync *)event ); ++ return; ++ } ++ + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); +@@ -153,7 +172,15 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_reset_event( (struct esync *)event ); ++ return; ++ } + event->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( event->esync_fd ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -177,6 +204,13 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ) + return event->signaled; + } + ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = event->manual_reset ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return event->esync_fd; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +@@ -214,6 +248,14 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (do_esync()) ++ close( event->esync_fd ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fd.c b/server/fd.c +index e5a4be7a3df..bbc2462163d 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -102,6 +102,7 @@ + #include "handle.h" + #include "process.h" + #include "request.h" ++#include "esync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -203,6 +204,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -216,6 +218,7 @@ static const struct object_ops fd_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -257,6 +260,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops inode_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -339,6 +344,7 @@ static const struct object_ops file_lock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1589,6 +1595,9 @@ static void fd_destroy( struct object *obj ) + free( fd->unlink_name ); + free( fd->unix_name ); + } ++ ++ if (do_esync()) ++ close( fd->esync_fd ); + } + + /* check if the desired access is possible without violating */ +@@ -1704,12 +1713,16 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1742,11 +1755,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->completion = NULL; + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); ++ ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 0, 0 ); + return fd; + } + +@@ -2172,6 +2189,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); ++ ++ if (do_esync() && !signaled) ++ esync_clear( fd->esync_fd ); + } + + /* check if events are pending and if yes return which one(s) */ +@@ -2203,6 +2223,15 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ int ret = fd->esync_fd; ++ *type = ESYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 23b6de275cd..49c4ba32b42 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -111,6 +111,7 @@ static const struct object_ops file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index bcbdea5b8ce..cae0ac1e395 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -102,6 +102,7 @@ extern void set_fd_signaled( struct fd *fd, int signaled ); + extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/handle.c b/server/handle.c +index c1fb4a490a9..cb5628b7e06 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -123,6 +123,7 @@ static const struct object_ops handle_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 2a3da247313..61b5014c442 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -81,6 +81,7 @@ static const struct object_ops hook_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index fb2a94cc7b8..18fef4b0466 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -78,6 +78,7 @@ static const struct object_ops mailslot_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -136,6 +137,7 @@ static const struct object_ops mail_writer_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -199,6 +201,7 @@ static const struct object_ops mailslot_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -229,6 +232,7 @@ static const struct object_ops mailslot_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 14ba74638ae..3e02cbb3832 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -36,6 +36,7 @@ + #include "thread.h" + #include "request.h" + #include "unicode.h" ++#include "esync.h" + + /* command-line options */ + int debug_level = 0; +@@ -140,6 +141,9 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_esync()) ++ esync_init(); ++ + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); + init_scheduler(); +diff --git a/server/mapping.c b/server/mapping.c +index 1da05412b6a..10def3ca694 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -68,6 +68,7 @@ static const struct object_ops ranges_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -104,6 +105,7 @@ static const struct object_ops shared_map_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +164,7 @@ static const struct object_ops mapping_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index b4f04ad307b..1235ab4731f 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -61,6 +61,7 @@ static const struct object_ops mutex_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index c83e8c17027..e59a5b6c183 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -118,6 +118,7 @@ static const struct object_ops named_pipe_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +163,7 @@ static const struct object_ops pipe_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -205,6 +207,7 @@ static const struct object_ops pipe_client_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -252,6 +255,7 @@ static const struct object_ops named_pipe_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -283,6 +287,7 @@ static const struct object_ops named_pipe_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 43f636b397b..5b6bb9cbfe1 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -68,6 +68,8 @@ struct object_ops + void (*remove_queue)(struct object *,struct wait_queue_entry *); + /* is object signaled? */ + int (*signaled)(struct object *,struct wait_queue_entry *); ++ /* return the esync fd for this object */ ++ int (*get_esync_fd)(struct object *, enum esync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index 1b6ddb1b982..df50955f621 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -49,6 +49,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + /* process object */ + +@@ -68,6 +69,7 @@ static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -78,6 +80,7 @@ static const struct object_ops process_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ ++ process_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -129,6 +132,7 @@ static const struct object_ops startup_info_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -175,6 +179,7 @@ static const struct object_ops job_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -541,6 +546,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + memset( &process->image_info, 0, sizeof(process->image_info) ); ++ process->esync_fd = -1; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -597,6 +603,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_esync()) ++ process->esync_fd = esync_create_fd( 0, 0 ); ++ + set_fd_events( process->msg_fd, POLLIN ); /* start listening to events */ + return process; + +@@ -645,6 +654,7 @@ static void process_destroy( struct object *obj ) + if (process->token) release_object( process->token ); + free( process->dir_cache ); + free( process->image ); ++ if (do_esync()) close( process->esync_fd ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -668,6 +678,13 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry + return !process->running_threads; + } + ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return process->esync_fd; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index 56092e5b1ac..eec69ddbcaf 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -98,6 +98,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + pe_image_info_t image_info; /* main exe image info */ ++ int esync_fd; /* esync file descriptor (signaled on exit) */ + }; + + /* process functions */ +diff --git a/server/protocol.def b/server/protocol.def +index 45f22624d5d..2a8662354f6 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3713,3 +3713,56 @@ struct handle_info + @REQ(resume_process) + obj_handle_t handle; /* process handle */ + @END ++ ++enum esync_type ++{ ++ ESYNC_SEMAPHORE = 1, ++ ESYNC_AUTO_EVENT, ++ ESYNC_MANUAL_EVENT, ++ ESYNC_MUTEX, ++ ESYNC_AUTO_SERVER, ++ ESYNC_MANUAL_SERVER, ++ ESYNC_QUEUE, ++}; ++ ++/* Create a new eventfd-based synchronization object */ ++@REQ(create_esync) ++ unsigned int access; /* wanted access rights */ ++ int initval; /* initial value */ ++ int type; /* type of esync object */ ++ int max; /* maximum count on a semaphore */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* actual type (may be different for events) */ ++ unsigned int shm_idx; ++@END ++ ++@REQ(open_esync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of esync object (above) */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of esync object (above) */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the esync fd for an object. */ ++@REQ(get_esync_fd) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++/* Notify the server that we are doing a message wait or done with one. */ ++@REQ(esync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++/* Retrieve the fd to wait on for user APCs. */ ++@REQ(get_esync_apc_fd) ++@END +diff --git a/server/queue.c b/server/queue.c +index 42b0f4e6bfd..a78748b96ca 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -43,6 +43,7 @@ + #include "process.h" + #include "request.h" + #include "user.h" ++#include "esync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -144,6 +145,8 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + int keystate_lock; /* owns an input keystate lock */ ++ int esync_fd; /* esync file descriptor (signalled on message) */ ++ int esync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -160,6 +163,7 @@ static void msg_queue_dump( struct object *obj, int verbose ); + static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -175,6 +179,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_add_queue, /* add_queue */ + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ ++ msg_queue_get_esync_fd, /* get_esync_fd */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -212,6 +217,7 @@ static const struct object_ops thread_input_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -313,11 +319,16 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->keystate_lock = 0; ++ queue->esync_fd = -1; ++ queue->esync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); ++ ++ if (do_esync()) ++ queue->esync_fd = esync_create_fd( 0, 0 ); + + thread->queue = queue; + } +@@ -497,6 +508,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + if (queue->keystate_lock) unlock_input_keystate( queue->input ); + queue->keystate_lock = 0; + } ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + + /* check whether msg is a keyboard message */ +@@ -955,6 +969,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (do_esync() && queue->esync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -1010,6 +1028,13 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + return ret || is_signaled( queue ); + } + ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = ESYNC_QUEUE; ++ return queue->esync_fd; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2491,6 +2516,9 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -3493,3 +3521,18 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(esync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->esync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index c00022ff63b..996bff5ef6d 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -167,6 +167,7 @@ static const struct object_ops key_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 97bf1a746d2..20b0ec309f3 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -96,6 +96,7 @@ static const struct object_ops master_socket_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a1f796f85b..d7d3a24e48f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -58,6 +58,7 @@ static const struct object_ops semaphore_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index 30fe6e8380f..a50ace9903f 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -92,6 +92,7 @@ static const struct object_ops serial_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index 7c2bf2cc154..b6d6dcfc4b6 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -67,6 +67,7 @@ static const struct object_ops handler_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index 07e1cf3a2ca..c2dfa8fb8ce 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -172,6 +172,7 @@ static const struct object_ops sock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1173,6 +1174,7 @@ static const struct object_ops ifchange_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1393,6 +1395,7 @@ static const struct object_ops socket_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 86ff09cd7e5..07f3c924f25 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -60,6 +60,7 @@ static const struct object_ops symlink_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 0e916e181bc..1a245c58396 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -51,6 +51,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + + /* thread queues */ +@@ -110,6 +111,7 @@ static const struct object_ops thread_apc_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -147,6 +149,7 @@ static const struct object_ops context_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -169,6 +172,7 @@ static const struct object_ops context_ops = + + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -182,6 +186,7 @@ static const struct object_ops thread_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ ++ thread_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -221,6 +226,8 @@ static inline void init_thread_structure( struct thread *thread ) + thread->context = NULL; + thread->teb = 0; + thread->entry_point = 0; ++ thread->esync_fd = -1; ++ thread->esync_apc_fd = -1; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -357,6 +364,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_esync()) ++ { ++ thread->esync_fd = esync_create_fd( 0, 0 ); ++ thread->esync_apc_fd = esync_create_fd( 0, 0 ); ++ } ++ + set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ + add_process_thread( thread->process, thread ); + return thread; +@@ -437,6 +450,9 @@ static void destroy_thread( struct object *obj ) + if (thread->exit_poll) remove_timeout_user( thread->exit_poll ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ ++ if (do_esync()) ++ close( thread->esync_fd ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -461,6 +477,13 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + return mythread->state == TERMINATED && !mythread->exit_poll; + } + ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return thread->esync_fd; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -1045,6 +1068,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_esync()) ++ esync_wake_up( obj ); ++ + LIST_FOR_EACH( ptr, &obj->wait_queue ) + { + struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); +@@ -1129,8 +1155,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (do_esync() && queue == &thread->user_apc) ++ esync_wake_fd( thread->esync_apc_fd ); ++ } ++ + return 1; + } + +@@ -1176,6 +1207,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); + } ++ ++ if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ esync_clear( thread->esync_apc_fd ); ++ + return apc; + } + +@@ -1292,6 +1327,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_esync()) ++ esync_abandon_mutexes( thread ); + wake_up( &thread->obj, 0 ); + if (violent_death) send_thread_signal( thread, SIGQUIT ); + cleanup_thread( thread ); +diff --git a/server/thread.h b/server/thread.h +index 78ca4c201b2..0f6108b684a 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -54,6 +54,8 @@ struct thread + struct process *process; + thread_id_t id; /* thread id */ + struct list mutex_list; /* list of currently owned mutexes */ ++ int esync_fd; /* esync file descriptor (signalled on exit) */ ++ int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index 9aba550fd93..dcbc9e2ece5 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -36,6 +36,7 @@ + #include "file.h" + #include "handle.h" + #include "request.h" ++#include "esync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -48,10 +49,12 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -65,6 +68,7 @@ static const struct object_ops timer_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ ++ timer_get_esync_fd, /* get_esync_fd */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +103,10 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->esync_fd = -1; ++ ++ if (do_esync()) ++ timer->esync_fd = esync_create_fd( 0, 0 ); + } + } + return timer; +@@ -172,6 +180,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( timer->esync_fd ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -205,6 +216,13 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ) + return timer->signaled; + } + ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return timer->esync_fd; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 68cfcf234c1..0f128728b0f 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -147,6 +147,7 @@ static const struct object_ops token_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 24059aac0fe..a79fda5ad80 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -108,6 +108,7 @@ static const struct object_ops window_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 883722eff36..1a031248a7c 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -64,6 +64,7 @@ static const struct object_ops winstation_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -89,6 +90,7 @@ static const struct object_ops desktop_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From 995fdfb8cdb0a8eed82e16640034dc9673ded681 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 12 Mar 2021 23:58:39 +0300 +Subject: [PATCH 14/16] esync: Fix restoring the objects state on wait all + objects retry. + +--- + dlls/ntdll/unix/esync.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 9a615fb277c..810477d02a0 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -1168,10 +1168,22 @@ tryagain: + { + /* We were too slow. Put everything back. */ + value = 1; +- for (j = i; j >= 0; j--) ++ for (j = i - 1; j >= 0; j--) + { +- if (write( obj->fd, &value, sizeof(value) ) == -1) ++ struct esync *obj = objs[j]; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ if (write( fds[j].fd, &value, sizeof(value) ) == -1) ++ { ++ ERR("write failed.\n"); + return errno_to_status( errno ); ++ } + } + + goto tryagain; /* break out of two loops and a switch */ +-- +2.30.2 + +From 69afcb164ccf8d3ecd5e94cf79c1e31698e14e5c Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Wed, 2 Feb 2022 17:02:44 -0500 +Subject: [PATCH] esync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/esync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 55c5695964d..4663374653a 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -526,6 +526,9 @@ NTSTATUS esync_set_event( HANDLE handle ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (obj->type == ESYNC_MANUAL_EVENT) + { + /* Acquire the spinlock. */ diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging-c14de4c.patch new file mode 100644 index 000000000..b68222d0c --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging-c14de4c.patch @@ -0,0 +1,4710 @@ +From c3e0c63e02d9a305d10b489b39b18adfe2e78393 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:51:21 +0200 +Subject: Esync rebased 5.13+ staging + + +diff --git a/README.esync b/README.esync +new file mode 100644 +index 00000000000..11d86563a10 +--- /dev/null ++++ b/README.esync +@@ -0,0 +1,196 @@ ++This is eventfd-based synchronization, or 'esync' for short. Turn it on with ++WINEESYNC=1; debug it with +esync. ++ ++== BUGS AND LIMITATIONS == ++ ++Please let me know if you find any bugs. If you can, also attach a log with +++seh,+pid,+esync,+server,+timestamp. ++ ++If you get something like "eventfd: Too many open files" and then things start ++crashing, you've probably run out of file descriptors. esync creates one ++eventfd descriptor for each synchronization object, and some games may use a ++large number of these. Linux by default limits a process to 4096 file ++descriptors, which probably was reasonable back in the nineties but isn't ++really anymore. (Fortunately Debian and derivatives [Ubuntu, Mint] already ++have a reasonable limit.) To raise the limit you'll want to edit ++/etc/security/limits.conf and add a line like ++ ++* hard nofile 1048576 ++ ++then restart your session. ++ ++On distributions using systemd, the settings in `/etc/security/limits.conf` ++will be overridden by systemd's own settings. If you run `ulimit -Hn` and it ++returns a lower number than the one you've previously set, then you can set ++ ++DefaultLimitNOFILE=1048576 ++ ++in both `/etc/systemd/system.conf` and `/etc/systemd/user.conf`. You can then ++execute `sudo systemctl daemon-reexec` and restart your session. Check again ++with `ulimit -Hn` that the limit is correct. ++ ++Also note that if the wineserver has esync active, all clients also must, and ++vice versa. Otherwise things will probably crash quite badly. ++ ++== EXPLANATION == ++ ++The aim is to execute all synchronization operations in "user-space", that is, ++without going through wineserver. We do this using Linux's eventfd ++facility. The main impetus to using eventfd is so that we can poll multiple ++objects at once; in particular we can't do this with futexes, or pthread ++semaphores, or the like. The only way I know of to wait on any of multiple ++objects is to use select/poll/epoll to wait on multiple fds, and eventfd gives ++us those fds in a quite usable way. ++ ++Whenever a semaphore, event, or mutex is created, we have the server, instead ++of creating a traditional server-side event/semaphore/mutex, instead create an ++'esync' primitive. These live in esync.c and are very slim objects; in fact, ++they don't even know what type of primitive they are. The server is involved ++at all because we still need a way of creating named objects, passing handles ++to another process, etc. ++ ++The server creates an eventfd file descriptor with the requested parameters ++and passes it back to ntdll. ntdll creates an object of the appropriate type, ++then caches it in a table. This table is copied almost wholesale from the fd ++cache code in server.c. ++ ++Specific operations follow quite straightforwardly from eventfd: ++ ++* To release an object, or set an event, we simply write() to it. ++* An object is signalled if read() succeeds on it. Notably, we create all ++ eventfd descriptors with O_NONBLOCK, so that we can atomically check if an ++ object is signalled and grab it if it is. This also lets us reset events. ++* For objects whose state should not be reset upon waiting—e.g. manual-reset ++ events—we simply check for the POLLIN flag instead of reading. ++* Semaphores are handled by the EFD_SEMAPHORE flag. This matches up quite well ++ (although with some difficulties; see below). ++* Mutexes store their owner thread locally. This isn't reliable information if ++ a different process's thread owns the mutex, but this doesn't matter—a ++ thread should only care whether it owns the mutex, so it knows whether to ++ try waiting on it or simply to increase the recursion count. ++ ++The interesting part about esync is that (almost) all waits happen in ntdll, ++including those on server-bound objects. The idea here is that on the server ++side, for any waitable object, we create an eventfd file descriptor (not an ++esync primitive), and then pass it to ntdll if the program tries to wait on ++it. These are cached too, so only the first wait will require a round trip to ++the server. Then the server signals the file descriptor as appropriate, and ++thereby wakes up the client. So far this is implemented for processes, ++threads, message queues (difficult; see below), and device managers (necessary ++for drivers to work). All of these are necessarily server-bound, so we ++wouldn't really gain anything by signalling on the client side instead. Of ++course, except possibly for message queues, it's not likely that any program ++(cutting-edge D3D game or not) is going to be causing a great wineserver load ++by waiting on any of these objects; the motivation was rather to provide a way ++to wait on ntdll-bound and server-bound objects at the same time. ++ ++Some cases are still passed to the server, and there's probably no reason not ++to keep them that way. Those that I noticed while testing include: async ++objects, which are internal to the file APIs and never exposed to userspace, ++startup_info objects, which are internal to the loader and signalled when a ++process starts, and keyed events, which are exposed through an ntdll API ++(although not through kernel32) but can't be mixed with other objects (you ++have to use NtWaitForKeyedEvent()). Other cases include: named pipes, debug ++events, sockets, and timers. It's unlikely we'll want to optimize debug events ++or sockets (or any of the other, rather rare, objects), but it is possible ++we'll want to optimize named pipes or timers. ++ ++There were two sort of complications when working out the above. The first one ++was events. The trouble is that (1) the server actually creates some events by ++itself and (2) the server sometimes manipulates events passed by the ++client. Resolving the first case was easy enough, and merely entailed creating ++eventfd descriptors for the events the same way as for processes and threads ++(note that we don't really lose anything this way; the events include ++"LowMemoryCondition" and the event that signals system processes to shut ++down). For the second case I basically had to hook the server-side event ++functions to redirect to esync versions if the event was actually an esync ++primitive. ++ ++The second complication was message queues. The difficulty here is that X11 ++signals events by writing into a pipe (at least I think it's a pipe?), and so ++as a result wineserver has to poll on that descriptor. In theory we could just ++let wineserver do so and then signal us as appropriate, except that wineserver ++only polls on the pipe when the thread is waiting for events (otherwise we'd ++get e.g. keyboard input while the thread is doing something else, and spin ++forever trying to wake up a thread that doesn't care). The obvious solution is ++just to poll on that fd ourselves, and that's what I did—it's just that ++getting the fd from wineserver was kind of ugly, and the code for waiting was ++also kind of ugly basically because we have to wait on both X11's fd and the ++"normal" process/thread-style wineserver fd that we use to signal sent ++messages. The upshot about the whole thing was that races are basically ++impossible, since a thread can only wait on its own queue. ++ ++System APCs already work, since the server will forcibly suspend a thread if ++it's not already waiting, and so we just need to check for EINTR from ++poll(). User APCs and alertable waits are implemented in a similar style to ++message queues (well, sort of): whenever someone executes an alertable wait, ++we add an additional eventfd to the list, which the server signals when an APC ++arrives. If that eventfd gets signaled, we hand it off to the server to take ++care of, and return STATUS_USER_APC. ++ ++Originally I kept the volatile state of semaphores and mutexes inside a ++variable local to the handle, with the knowledge that this would break if ++someone tried to open the handle elsewhere or duplicate it. It did, and so now ++this state is stored inside shared memory. This is of the POSIX variety, is ++allocated by the server (but never mapped there) and lives under the path ++"/wine-esync". ++ ++There are a couple things that this infrastructure can't handle, although ++surprisingly there aren't that many. In particular: ++* Implementing wait-all, i.e. WaitForMultipleObjects(..., TRUE, ...), is not ++ exactly possible the way we'd like it to be possible. In theory that ++ function should wait until it knows all objects are available, then grab ++ them all at once atomically. The server (like the kernel) can do this ++ because the server is single-threaded and can't race with itself. We can't ++ do this in ntdll, though. The approach I've taken I've laid out in great ++ detail in the relevant patch, but for a quick summary we poll on each object ++ until it's signaled (but don't grab it), check them all again, and if ++ they're all signaled we try to grab them all at once in a tight loop, and if ++ we fail on any of them we reset the count on whatever we shouldn't have ++ consumed. Such a blip would necessarily be very quick. ++* The whole patchset only works on Linux, where eventfd is available. However, ++ it should be possible to make it work on a Mac, since eventfd is just a ++ quicker, easier way to use pipes (i.e. instead of writing 1 to the fd you'd ++ write 1 byte; instead of reading a 64-bit value from the fd you'd read as ++ many bytes as you can carry, which is admittedly less than 2**64 but ++ can probably be something reasonable.) It's also possible, although I ++ haven't yet looked, to use some different kind of synchronization ++ primitives, but pipes would be easiest to tack onto this framework. ++* PulseEvent() can't work the way it's supposed to work. Fortunately it's rare ++ and deprecated. It's also explicitly mentioned on MSDN that a thread can ++ miss the notification for a kernel APC, so in a sense we're not necessarily ++ doing anything wrong. ++ ++There are some things that are perfectly implementable but that I just haven't ++done yet: ++* Other synchronizable server primitives. It's unlikely we'll need any of ++ these, except perhaps named pipes (which would honestly be rather difficult) ++ and (maybe) timers. ++* Access masks. We'd need to store these inside ntdll, and validate them when ++ someone tries to execute esync operations. ++ ++This patchset was inspired by Daniel Santos' "hybrid synchronization" ++patchset. My idea was to create a framework whereby even contended waits could ++be executed in userspace, eliminating a lot of the complexity that his ++synchronization primitives used. I do however owe some significant gratitude ++toward him for setting me on the right path. ++ ++I've tried to maximize code separation, both to make any potential rebases ++easier and to ensure that esync is only active when configured. All code in ++existing source files is guarded with "if (do_esync())", and generally that ++condition is followed by "return esync_version_of_this_method(...);", where ++the latter lives in esync.c and is declared in esync.h. I've also tried to ++make the patchset very clear and readable—to write it as if I were going to ++submit it upstream. (Some intermediate patches do break things, which Wine is ++generally against, but I think it's for the better in this case.) I have cut ++some corners, though; there is some error checking missing, or implicit ++assumptions that the program is behaving correctly. ++ ++I've tried to be careful about races. There are a lot of comments whose ++purpose are basically to assure me that races are impossible. In most cases we ++don't have to worry about races since all of the low-level synchronization is ++done by the kernel. ++ ++Anyway, yeah, this is esync. Use it if you like. ++ ++--Zebediah Figura +diff --git a/configure b/configure +index 5616f03e9ad..6c4068f8abb 100755 +--- a/configure ++++ b/configure +@@ -7553,6 +7553,7 @@ for ac_header in \ + sys/cdio.h \ + sys/epoll.h \ + sys/event.h \ ++ sys/eventfd.h \ + sys/filio.h \ + sys/ioctl.h \ + sys/ipc.h \ +@@ -18355,6 +18356,7 @@ for ac_func in \ + pipe2 \ + poll \ + port_create \ ++ ppoll \ + prctl \ + pread \ + proc_pidinfo \ +@@ -18766,6 +18768,72 @@ fi + ;; + esac + ++if test "$ac_cv_header_sys_mman_h" = "yes" -a "x$RT_LIBS" = "x" ++then ++ ac_save_LIBS=$LIBS ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing shm_open" >&5 ++$as_echo_n "checking for library containing shm_open... " >&6; } ++if ${ac_cv_search_shm_open+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_func_search_save_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 shm_open (); ++int ++main () ++{ ++return shm_open (); ++ ; ++ return 0; ++} ++_ACEOF ++for ac_lib in '' rt; do ++ if test -z "$ac_lib"; then ++ ac_res="none required" ++ else ++ ac_res=-l$ac_lib ++ LIBS="-l$ac_lib $ac_func_search_save_LIBS" ++ fi ++ if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_search_shm_open=$ac_res ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext ++ if ${ac_cv_search_shm_open+:} false; then : ++ break ++fi ++done ++if ${ac_cv_search_shm_open+:} false; then : ++ ++else ++ ac_cv_search_shm_open=no ++fi ++rm conftest.$ac_ext ++LIBS=$ac_func_search_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5 ++$as_echo "$ac_cv_search_shm_open" >&6; } ++ac_res=$ac_cv_search_shm_open ++if test "$ac_res" != no; then : ++ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" ++ ++$as_echo "#define HAVE_SHM_OPEN 1" >>confdefs.h ++ ++ test "$ac_res" = "none required" || RT_LIBS="$ac_res" ++ ++fi ++ ++fi ++LIBS=$ac_save_LIBS ++ + if test "x$with_ldap" != "xno" + then + if ${LDAP_CFLAGS:+false} :; then : +diff --git a/configure.ac b/configure.ac +index dd925caf312..f1084515b7a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -507,6 +507,7 @@ AC_CHECK_HEADERS(\ + sys/cdio.h \ + sys/epoll.h \ + sys/event.h \ ++ sys/eventfd.h \ + sys/filio.h \ + sys/ioctl.h \ + sys/ipc.h \ +@@ -2260,6 +2261,7 @@ AC_CHECK_FUNCS(\ + pipe2 \ + poll \ + port_create \ ++ ppoll \ + prctl \ + pread \ + proc_pidinfo \ +@@ -2324,6 +2326,16 @@ case $host_os in + ;; + esac + ++dnl Check for shm_open which may be in -lrt ++if test "$ac_cv_header_sys_mman_h" = "yes" -a "x$RT_LIBS" = "x" ++then ++ ac_save_LIBS=$LIBS ++ AC_SEARCH_LIBS(shm_open, rt, ++ [AC_DEFINE(HAVE_SHM_OPEN, 1, [Define to 1 if you have the `shm_open' function.]) ++ test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")]) ++fi ++LIBS=$ac_save_LIBS ++ + dnl **** Check for OpenLDAP *** + if test "x$with_ldap" != "xno" + then +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 2a47abf342c..0f072a5846e 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -54,6 +54,7 @@ static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK); + + static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG_PTR, SIZE_T *, ULONG, ULONG); + static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG); ++static NTSTATUS (WINAPI *pNtQuerySystemTime)(LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *); + static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*); + static PSLIST_ENTRY (__fastcall *pRtlInterlockedPushListSList)(PSLIST_HEADER list, PSLIST_ENTRY first, +@@ -177,8 +178,23 @@ static void test_signalandwait(void) + CloseHandle(file); + } + ++static HANDLE mutex, mutex2, mutices[2]; ++ ++static DWORD WINAPI mutex_thread( void *param ) ++{ ++ DWORD expect = (DWORD)(DWORD_PTR)param; ++ DWORD ret; ++ ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == expect, "expected %u, got %u\n", expect, ret); ++ ++ if (!ret) ReleaseMutex( mutex ); ++ return 0; ++} ++ + static void test_mutex(void) + { ++ HANDLE thread; + DWORD wait_ret; + BOOL ret; + HANDLE hCreated; +@@ -218,7 +234,8 @@ todo_wine + SetLastError(0xdeadbeef); + hOpened = OpenMutexA(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex"); + ok(hOpened != NULL, "OpenMutex failed with error %d\n", GetLastError()); +- wait_ret = WaitForSingleObject(hOpened, INFINITE); ++ wait_ret = WaitForSingleObject(hOpened, 0); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: validation is not implemented */ + ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n"); + CloseHandle(hOpened); + +@@ -249,6 +266,7 @@ todo_wine + + SetLastError(0xdeadbeef); + ret = ReleaseMutex(hCreated); ++todo_wine_if(getenv("WINEESYNC")) /* XFAIL: due to the above */ + ok(!ret && (GetLastError() == ERROR_NOT_OWNER), + "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %d\n", GetLastError()); + +@@ -287,6 +305,85 @@ todo_wine + CloseHandle(hOpened); + + CloseHandle(hCreated); ++ ++ mutex = CreateMutexA( NULL, FALSE, NULL ); ++ ok(!!mutex, "got error %u\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %d\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( mutex, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ } ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %u\n", GetLastError()); ++ } ++ ++ ret = ReleaseMutex( mutex ); ++ ok(!ret, "got %d\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %u\n", ret); ++ ++ WaitForSingleObject( mutex, 0 ); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)WAIT_TIMEOUT, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %u\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL ); ++ ret = WaitForSingleObject( thread, 2000 ); ++ ok(ret == 0, "wait failed: %u\n", ret); ++ ++ mutex2 = CreateMutexA( NULL, TRUE, NULL ); ++ ok(!!mutex2, "got error %u\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %d\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\n", GetLastError()); ++ ++ mutices[0] = mutex; ++ mutices[1] = mutex2; ++ ++ ret = WaitForMultipleObjects( 2, mutices, FALSE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(!ret, "got %d\n", ret); ++ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\n", GetLastError()); ++ ++ ret = WaitForMultipleObjects( 2, mutices, TRUE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = ReleaseMutex( mutex ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ReleaseMutex( mutex2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = CloseHandle( mutex ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = CloseHandle( mutex2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ + } + + static void test_slist(void) +@@ -462,12 +559,13 @@ static void test_slist(void) + + static void test_event(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + ACL acl; + DWORD ret; + BOOL val; ++ int i; + + /* no sd */ + handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event"); +@@ -571,11 +669,130 @@ static void test_event(void) + ok( ret, "QueryMemoryResourceNotification failed err %u\n", GetLastError() ); + ok( val == FALSE || val == TRUE, "wrong value %u\n", val ); + CloseHandle( handle ); ++ ++ handle = CreateEventA( NULL, TRUE, FALSE, NULL ); ++ ok(!!handle, "got error %u\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = SetEvent( handle ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ for (i = 0; i < 100; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ } ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ResetEvent( handle ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ handle2 = CreateEventA( NULL, FALSE, TRUE, NULL ); ++ ok(!!handle2, "got error %u\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = SetEvent( handle2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = ResetEvent( handle2 ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ResetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ SetEvent( handle2 ); ++ ResetEvent( handle ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ handles[0] = handle2; ++ handles[1] = handle; ++ SetEvent( handle ); ++ SetEvent( handle2 ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %u\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %u\n", GetLastError()); + } + + static void test_semaphore(void) + { +- HANDLE handle, handle2; ++ HANDLE handle, handle2, handles[2]; ++ DWORD ret; ++ LONG prev; ++ int i; + + /* test case sensitivity */ + +@@ -617,6 +834,99 @@ static void test_semaphore(void) + ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError()); + + CloseHandle( handle ); ++ ++ handle = CreateSemaphoreA( NULL, 0, 5, NULL ); ++ ok(!!handle, "CreateSemaphore failed: %u\n", GetLastError()); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ok(prev == 0, "got prev %d\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ok(prev == 1, "got prev %d\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 5, &prev ); ++ ok(!ret, "got %d\n", ret); ++ ok(GetLastError() == ERROR_TOO_MANY_POSTS, "got error %u\n", GetLastError()); ++ ok(prev == 1, "got prev %d\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 2, &prev ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ok(prev == 2, "got prev %d\n", prev); ++ ++ ret = ReleaseSemaphore( handle, 1, &prev ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ok(prev == 4, "got prev %d\n", prev); ++ ++ for (i = 0; i < 5; i++) ++ { ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ handle2 = CreateSemaphoreA( NULL, 3, 5, NULL ); ++ ok(!!handle2, "CreateSemaphore failed: %u\n", GetLastError()); ++ ++ ret = ReleaseSemaphore( handle2, 1, &prev ); ++ ok(ret, "got error %u\n", GetLastError()); ++ ok(prev == 3, "got prev %d\n", prev); ++ ++ for (i = 0; i < 4; i++) ++ { ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ } ++ ++ ret = WaitForSingleObject( handle2, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ handles[0] = handle; ++ handles[1] = handle2; ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == 1, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ReleaseSemaphore( handle2, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ReleaseSemaphore( handle, 1, NULL ); ++ ++ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ ret = WaitForSingleObject( handle, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = CloseHandle( handle ); ++ ok(ret, "got error %u\n", ret); ++ ++ ret = CloseHandle( handle2 ); ++ ok(ret, "got error %u\n", ret); + } + + static void test_waitable_timer(void) +@@ -1171,11 +1481,15 @@ static HANDLE modify_handle(HANDLE handle, DWORD modify) + return ULongToHandle(tmp); + } + ++#define TIMEOUT_INFINITE (((LONGLONG)0x7fffffff) << 32 | 0xffffffff) ++ + static void test_WaitForSingleObject(void) + { + HANDLE signaled, nonsignaled, invalid; ++ LARGE_INTEGER ntnow, ntthen; + LARGE_INTEGER timeout; + NTSTATUS status; ++ DWORD now, then; + DWORD ret; + + signaled = CreateEventW(NULL, TRUE, TRUE, NULL); +@@ -1260,6 +1574,68 @@ static void test_WaitForSingleObject(void) + status = pNtWaitForSingleObject(GetCurrentThread(), FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "expected STATUS_TIMEOUT, got %08x\n", status); + ++ ret = WaitForSingleObject( signaled, 0 ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ ret = WaitForSingleObject( nonsignaled, 0 ); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ++ /* test that a timed wait actually does wait */ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( nonsignaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == WAIT_TIMEOUT, "got %u\n", ret); ++ ok(abs((then - now) - 100) < 5, "got %u ms\n", then - now); ++ ++ now = GetTickCount(); ++ ret = WaitForSingleObject( signaled, 100 ); ++ then = GetTickCount(); ++ ok(ret == 0, "got %u\n", ret); ++ ok(abs(then - now) < 5, "got %u ms\n", then - now); ++ ++ ret = WaitForSingleObject( signaled, INFINITE ); ++ ok(ret == 0, "got %u\n", ret); ++ ++ /* test NT timeouts */ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart + 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#x\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = -100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#x\n", status); ++ ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ status = pNtWaitForSingleObject( signaled, FALSE, NULL ); ++ ok(status == 0, "got %#x\n", status); ++ ++ timeout.QuadPart = TIMEOUT_INFINITE; ++ status = pNtWaitForSingleObject( signaled, FALSE, &timeout ); ++ ok(status == 0, "got %#x\n", status); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#x\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ ++ pNtQuerySystemTime( &ntnow ); ++ timeout.QuadPart = ntnow.QuadPart - 100 * 10000; ++ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout ); ++ pNtQuerySystemTime( &ntthen ); ++ ok(status == STATUS_TIMEOUT, "got %#x\n", status); ++ ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n", ++ wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100)); ++ + CloseHandle(signaled); + CloseHandle(nonsignaled); + } +@@ -2702,6 +3078,84 @@ static void test_crit_section(void) + ok(cs.DebugInfo == NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + } + ++static int zigzag_state, zigzag_count[2], zigzag_stop; ++ ++static DWORD CALLBACK zigzag_event0(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[0], INFINITE); ++ ResetEvent(events[0]); ++ ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state); ++ zigzag_state++; ++ SetEvent(events[1]); ++ zigzag_count[0]++; ++ } ++ trace("thread 0 got done\n"); ++ return 0; ++} ++ ++static DWORD CALLBACK zigzag_event1(void *arg) ++{ ++ HANDLE *events = arg; ++ ++ while (!zigzag_stop) ++ { ++ WaitForSingleObject(events[1], INFINITE); ++ ResetEvent(events[1]); ++ ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state); ++ zigzag_state--; ++ SetEvent(events[0]); ++ zigzag_count[1]++; ++ } ++ trace("thread 1 got done\n"); ++ return 0; ++} ++ ++static void test_zigzag_event(void) ++{ ++ /* The basic idea is to test SetEvent/Wait back and forth between two ++ * threads. Each thread clears their own event, sets some common data, ++ * signals the other's, then waits on their own. We make sure the common ++ * data is always in the right state. We also print performance data. */ ++ ++ HANDLE threads[2], events[2]; ++ BOOL ret; ++ ++ events[0] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ events[1] = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL); ++ threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL); ++ ++ zigzag_state = 0; ++ zigzag_count[0] = zigzag_count[1] = 0; ++ zigzag_stop = 0; ++ ++ trace("starting zigzag test (events)\n"); ++ SetEvent(events[0]); ++ Sleep(2000); ++ zigzag_stop = 1; ++ ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE); ++ trace("%d\n", ret); ++ ok(ret == 0 || ret == 1, "wait failed: %u\n", ret); ++ ++ ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1, ++ "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]); ++ ++ /* signal the other thread to finish, if it didn't already ++ * (in theory they both would at the same time, but there's a slight race on teardown if we get ++ * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */ ++ zigzag_state = 1-ret; ++ SetEvent(events[1-ret]); ++ ret = WaitForSingleObject(threads[1-ret], 1000); ++ ok(!ret, "wait failed: %u\n", ret); ++ ++ trace("count: %d\n", zigzag_count[0]); ++} ++ + START_TEST(sync) + { + char **argv; +@@ -2728,6 +3182,7 @@ START_TEST(sync) + pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared"); + pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory"); + pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory"); ++ pNtQuerySystemTime = (void *)GetProcAddress(hntdll, "NtQuerySystemTime"); + pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject"); + pNtWaitForMultipleObjects = (void *)GetProcAddress(hntdll, "NtWaitForMultipleObjects"); + pRtlInterlockedPushListSList = (void *)GetProcAddress(hntdll, "RtlInterlockedPushListSList"); +@@ -2763,5 +3218,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index 67847bb9392..c96a62ae006 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -46,6 +46,7 @@ C_SRCS = \ + unix/cdrom.c \ + unix/debug.c \ + unix/env.c \ ++ unix/esync.c \ + unix/file.c \ + unix/loader.c \ + unix/process.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +new file mode 100644 +index 00000000000..ed801c71991 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.c +@@ -0,0 +1,1331 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_POLL_H ++#include ++#endif ++#include ++#include ++#include ++#ifdef HAVE_SYS_MMAN_H ++# include ++#endif ++#ifdef HAVE_SYS_POLL_H ++# include ++#endif ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#define NONAMELESSUNION ++#include "windef.h" ++#include "winternl.h" ++#include "wine/server.h" ++#include "wine/debug.h" ++ ++#include "unix_private.h" ++#include "esync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(esync); ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("eventfd not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct esync ++{ ++ enum esync_type type; ++ int fd; ++ void *shm; ++}; ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define ESYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct esync)) ++#define ESYNC_LIST_ENTRIES 256 ++ ++static struct esync *esync_list[ESYNC_LIST_ENTRIES]; ++static struct esync esync_list_initial_block[ESYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / ESYNC_LIST_BLOCK_SIZE; ++ return idx % ESYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!esync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) esync_list[0] = esync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ esync_list[entry] = ptr; ++ } ++ } ++ ++ if (!InterlockedCompareExchange( (int *)&esync_list[entry][idx].type, type, 0 )) ++ { ++ esync_list[entry][idx].fd = fd; ++ esync_list[entry][idx].shm = shm; ++ } ++ return &esync_list[entry][idx]; ++} ++ ++static struct esync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= ESYNC_LIST_ENTRIES || !esync_list[entry]) return NULL; ++ if (!esync_list[entry][idx].type) return NULL; ++ ++ return &esync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper esync object (i.e. an event, ++ * semaphore, etc. created using create_esync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct esync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ enum esync_type type = 0; ++ unsigned int shm_idx = 0; ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ if (!handle) ++ { ++ /* Shadow of the Tomb Raider really likes passing in NULL handles to ++ * various functions. Concerning, but let's avoid a server call. */ ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ if (!(*obj = get_cached_object( handle ))) ++ { ++ SERVER_START_REQ( get_esync_fd ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == handle ); ++ } ++ } ++ SERVER_END_REQ; ++ } ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (*obj) ++ { ++ /* We managed to grab it while in the CS; return it. */ ++ return STATUS_SUCCESS; ++ } ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve fd for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got fd %d for handle %p.\n", fd, handle); ++ ++ *obj = add_to_list( handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ return ret; ++} ++ ++NTSTATUS esync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < ESYNC_LIST_ENTRIES && esync_list[entry]) ++ { ++ if (InterlockedExchange((int *)&esync_list[entry][idx].type, 0)) ++ { ++ close( esync_list[entry][idx].fd ); ++ return STATUS_SUCCESS; ++ } ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_esync( enum esync_type type, HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, int initval, int max ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ /* We have to synchronize on the fd cache CS so that our calls to ++ * receive_fd don't race with theirs. */ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( create_esync ) ++ { ++ req->access = access; ++ req->initval = initval; ++ req->type = type; ++ req->max = max; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_esync( enum esync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ obj_handle_t fd_handle; ++ unsigned int shm_idx; ++ sigset_t sigset; ++ int fd; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( open_esync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ fd = receive_fd( &fd_handle ); ++ assert( wine_server_ptr_handle(fd_handle) == *handle ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 ); ++ ++ TRACE("-> handle %p, fd %d.\n", *handle, fd); ++ } ++ return ret; ++} ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ uint64_t count64 = count; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (InterlockedCompareExchange( &semaphore->count, count + current, current ) != current); ++ ++ if (prev) *prev = current; ++ ++ /* We don't have to worry about a race between increasing the count and ++ * write(). The fact that we were able to increase the count means that we ++ * have permission to actually write that many releases to the semaphore. */ ++ ++ if (write( obj->fd, &count64, sizeof(count64) ) == -1) ++ return errno_to_status( errno ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_esync( type, handle, access, attr, initial, 0 ); ++} ++ ++NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */ ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Manual-reset events are actually racier than other objects in terms of shm ++ * state. With other objects, races don't matter, because we only treat the shm ++ * state as a hint that lets us skip poll()—we still have to read(). But with ++ * manual-reset events we don't, which means that the shm state can be out of ++ * sync with the actual state. ++ * ++ * In general we shouldn't have to worry about races between modifying the ++ * event and waiting on it. If the state changes while we're waiting, it's ++ * equally plausible that we caught it before or after the state changed. ++ * However, we can have races between SetEvent() and ResetEvent(), so that the ++ * event has inconsistent internal state. ++ * ++ * To solve this we have to use the other field to lock the event. Currently ++ * this is implemented as a spinlock, but I'm not sure if a futex might be ++ * better. I'm also not sure if it's possible to obviate locking by arranging ++ * writes and reads in a certain way. ++ * ++ * Note that we don't have to worry about locking in esync_wait_objects(). ++ * There's only two general patterns: ++ * ++ * WaitFor() SetEvent() ++ * ------------------------- ++ * read() ++ * signaled = 0 ++ * signaled = 1 ++ * write() ++ * ------------------------- ++ * read() ++ * signaled = 1 ++ * signaled = 0 ++ * ++ * ------------------------- ++ * ++ * That is, if SetEvent() tries to signal the event before WaitFor() resets its ++ * signaled state, it won't bother trying to write(), and then the signaled ++ * state will be reset, so the result is a consistent non-signaled event. ++ * There's several variations to this pattern but all of them are protected in ++ * the same way. Note however this is why we have to use interlocked_xchg() ++ * event inside of the lock. ++ */ ++ ++/* Removing this spinlock is harder than it looks. esync_wait_objects() can ++ * deal with inconsistent state well enough, and a race between SetEvent() and ++ * ResetEvent() gives us license to yield either result as long as we act ++ * consistently, but that's not enough. Notably, esync_wait_objects() should ++ * probably act like a fence, so that the second half of esync_set_event() does ++ * not seep past a subsequent reset. That's one problem, but no guarantee there ++ * aren't others. */ ++ ++NTSTATUS esync_set_event( HANDLE handle ) ++{ ++ static const uint64_t value = 1; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling write() if the event wasn't already ++ * signaled. ++ * ++ * For auto-reset events, esync_wait_objects() must grab the kernel object. ++ * Thus if we got into a race so that the shm state is signaled but the ++ * eventfd is unsignaled (i.e. reset shm, set shm, set fd, reset fd), we ++ * *must* signal the fd now, or any waiting threads will never wake up. */ ++ ++ if (!InterlockedExchange( &event->signaled, 1 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ ERR("write: %s\n", strerror(errno)); ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_reset_event( HANDLE handle ) ++{ ++ uint64_t value; ++ struct esync *obj; ++ struct event *event; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (InterlockedCompareExchange( &event->locked, 1, 0 )) ++ small_pause(); ++ } ++ ++ /* For manual-reset events, as long as we're in a lock, we can take the ++ * optimization of only calling read() if the event was already signaled. ++ * ++ * For auto-reset events, we have no guarantee that the previous "signaled" ++ * state is actually correct. We need to leave both states unsignaled after ++ * leaving this function, so we always have to read(). */ ++ if (InterlockedExchange( &event->signaled, 0 ) || obj->type == ESYNC_AUTO_EVENT) ++ { ++ if (read( obj->fd, &value, sizeof(value) ) == -1 && errno != EWOULDBLOCK && errno != EAGAIN) ++ { ++ ERR("read: %s\n", strerror(errno)); ++ } ++ } ++ ++ if (obj->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_pulse_event( HANDLE handle ) ++{ ++ uint64_t value = 1; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ NtYieldExecution(); ++ ++ read( obj->fd, &value, sizeof(value) ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ struct pollfd fd; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ ++ fd.fd = obj->fd; ++ fd.events = POLLIN; ++ out->EventState = poll( &fd, 1, 0 ); ++ out->EventType = (obj->type == ESYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 ); ++} ++ ++NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_esync( ESYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ static const uint64_t value = 1; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ /* This is thread-safe, because the only thread that can change the tid to ++ * or from our tid is ours. */ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ mutex->count--; ++ ++ if (!mutex->count) ++ { ++ /* This is also thread-safe, as long as signaling the file is the last ++ * thing we do. Other threads don't care about the tid if it isn't ++ * theirs. */ ++ mutex->tid = 0; ++ ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct esync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++#define TICKSPERSEC 10000000 ++#define TICKSPERMSEC 10000 ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end ) ++{ ++ int ret; ++ ++ do ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ ++#ifdef HAVE_PPOLL ++ /* We use ppoll() if available since the time granularity is better. */ ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = ppoll( fds, nfds, &tmo_p, NULL ); ++#else ++ ret = poll( fds, nfds, timeleft / TICKSPERMSEC ); ++#endif ++ } ++ else ++ ret = poll( fds, nfds, -1 ); ++ ++ /* If we receive EINTR we were probably suspended (SIGUSR1), possibly for a ++ * system APC. The right thing to do is just try again. */ ++ } while (ret < 0 && errno == EINTR); ++ ++ return ret; ++} ++ ++/* Return TRUE if abandoned. */ ++static BOOL update_grabbed_object( struct esync *obj ) ++{ ++ BOOL ret = FALSE; ++ ++ if (obj->type == ESYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we grabbed it means the count is now zero, so nobody else ++ * can (and the only thread that can release it is us). */ ++ if (mutex->tid == ~0) ++ ret = TRUE; ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ } ++ else if (obj->type == ESYNC_SEMAPHORE) ++ { ++ struct semaphore *semaphore = obj->shm; ++ /* We don't have to worry about a race between this and read(); the ++ * fact that we were able to grab it at all means the count is nonzero, ++ * and if someone else grabbed it then the count must have been >= 2, ++ * etc. */ ++ InterlockedExchangeAdd( &semaphore->count, -1 ); ++ } ++ else if (obj->type == ESYNC_AUTO_EVENT) ++ { ++ struct event *event = obj->shm; ++ /* We don't have to worry about a race between this and read(), since ++ * this is just a hint, and the real state is in the kernel object. ++ * This might already be 0, but that's okay! */ ++ event->signaled = 0; ++ } ++ ++ return ret; ++} ++ ++/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we ++ * need to delegate to server_select(). */ ++static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero; ++ ++ struct esync *objs[MAXIMUM_WAIT_OBJECTS]; ++ struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1]; ++ int has_esync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD pollcount; ++ ULONGLONG end; ++ int64_t value; ++ ssize_t size; ++ int i, j, ret; ++ ++ /* Grab the APC fd if we don't already have it. */ ++ if (alertable && ntdll_get_thread_data()->esync_apc_fd == -1) ++ { ++ obj_handle_t fd_handle; ++ sigset_t sigset; ++ int fd = -1; ++ ++ server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ SERVER_START_REQ( get_esync_apc_fd ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ { ++ fd = receive_fd( &fd_handle ); ++ assert( fd_handle == GetCurrentThreadId() ); ++ } ++ } ++ SERVER_END_REQ; ++ server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); ++ ++ ntdll_get_thread_data()->esync_apc_fd = fd; ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart >= 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_esync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_esync && has_server) ++ FIXME("Can't wait on esync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(esync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) timeleft / TICKSPERSEC, (long) timeleft % TICKSPERSEC); ++ } ++ } ++ ++ if (wait_any || count == 1) ++ { ++ /* Try to check objects now, so we can obviate poll() at least. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (obj) ++ { ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ else if (!mutex->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ if (mutex->tid == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ i += STATUS_ABANDONED_WAIT_0; ++ } ++ else ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->tid = GetCurrentThreadId(); ++ mutex->count++; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ ++ if (semaphore->count) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ InterlockedDecrement( &semaphore->count ); ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ event->signaled = 0; ++ return i; ++ } ++ } ++ break; ++ } ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = obj->shm; ++ ++ if (event->signaled) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ break; ++ } ++ case ESYNC_AUTO_SERVER: ++ case ESYNC_MANUAL_SERVER: ++ case ESYNC_QUEUE: ++ /* We can't wait on any of these. Fortunately I don't think ++ * they'll ever be uncontended anyway (at least, they won't be ++ * performance-critical). */ ++ break; ++ } ++ } ++ ++ fds[i].fd = obj ? obj->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ if (alertable) ++ { ++ fds[i].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[i].events = POLLIN; ++ i++; ++ } ++ pollcount = i; ++ ++ while (1) ++ { ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret > 0) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (alertable) ++ { ++ if (fds[pollcount - 1].revents & POLLIN) ++ goto userapc; ++ } ++ ++ /* Find out which object triggered the wait. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[i].fd, fds[i].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ if (obj) ++ { ++ if (obj->type == ESYNC_MANUAL_EVENT ++ || obj->type == ESYNC_MANUAL_SERVER ++ || obj->type == ESYNC_QUEUE) ++ { ++ /* Don't grab the object, just check if it's signaled. */ ++ if (fds[i].revents & POLLIN) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ } ++ else ++ { ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value)) ++ { ++ /* We found our object. */ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (update_grabbed_object( obj )) ++ return STATUS_ABANDONED_WAIT_0 + i; ++ return i; ++ } ++ } ++ } ++ } ++ ++ /* If we got here, someone else stole (or reset, etc.) whatever ++ * we were waiting for. So keep waiting. */ ++ NtQuerySystemTime( &now ); ++ } ++ else ++ goto err; ++ } ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ while (1) ++ { ++tryagain: ++ /* First step: try to poll on each object in sequence. */ ++ fds[0].events = POLLIN; ++ pollcount = 1; ++ if (alertable) ++ { ++ /* We also need to wait on APCs. */ ++ fds[1].fd = ntdll_get_thread_data()->esync_apc_fd; ++ fds[1].events = POLLIN; ++ pollcount++; ++ } ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ fds[0].fd = obj ? obj->fd : -1; ++ ++ if (obj && obj->type == ESYNC_MUTEX) ++ { ++ /* It might be ours. */ ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ } ++ ++ ret = do_poll( fds, pollcount, timeout ? &end : NULL ); ++ if (ret <= 0) ++ goto err; ++ else if (alertable && (fds[1].revents & POLLIN)) ++ goto userapc; ++ ++ if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL)) ++ { ++ ERR("Polling on fd %d returned %#x.\n", fds[0].fd, fds[0].revents); ++ return STATUS_INVALID_HANDLE; ++ } ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ fds[i].fd = objs[i] ? objs[i]->fd : -1; ++ fds[i].events = POLLIN; ++ } ++ /* There's no reason to check for APCs here. */ ++ pollcount = i; ++ ++ /* Poll everything to see if they're still signaled. */ ++ ret = poll( fds, pollcount, 0 ); ++ if (ret == pollcount) ++ { ++ BOOL abandoned = FALSE; ++ ++ /* Quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct esync *obj = objs[i]; ++ ++ switch (obj->type) ++ { ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ if (mutex->tid == GetCurrentThreadId()) ++ break; ++ /* otherwise fall through */ ++ } ++ case ESYNC_SEMAPHORE: ++ case ESYNC_AUTO_EVENT: ++ if ((size = read( fds[i].fd, &value, sizeof(value) )) != sizeof(value)) ++ { ++ /* We were too slow. Put everything back. */ ++ value = 1; ++ for (j = i; j >= 0; j--) ++ { ++ if (write( obj->fd, &value, sizeof(value) ) == -1) ++ return errno_to_status( errno ); ++ } ++ ++ goto tryagain; /* break out of two loops and a switch */ ++ } ++ break; ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. */ ++ /* Make sure to let ourselves know that we grabbed the mutexes ++ * and semaphores. */ ++ for (i = 0; i < count; i++) ++ abandoned |= update_grabbed_object( objs[i] ); ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ } ++ ++ /* If we got here, ppoll() returned less than all of our objects. ++ * So loop back to the beginning and try again. */ ++ } /* while(1) */ ++ } /* else (wait-all) */ ++ ++err: ++ /* We should only get here if poll() failed. */ ++ ++ if (ret == 0) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else ++ { ++ ERR("ppoll failed: %s\n", strerror(errno)); ++ return errno_to_status( errno ); ++ } ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_select(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* We need to let the server know when we are doing a message wait, and when we ++ * are done with one, so that all of the code surrounding hung queues works. ++ * We also need this for WaitForInputIdle(). */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( esync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from esync_wait_objects(). */ ++NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == ESYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct esync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case ESYNC_SEMAPHORE: ++ ret = esync_release_semaphore( signal, 1, NULL ); ++ break; ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ ret = esync_set_event( signal ); ++ break; ++ case ESYNC_MUTEX: ++ ret = esync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return esync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_esync()) ++ { ++ /* make sure the server isn't running with WINEESYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_esync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEESYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open esync shared memory file; make sure no stale wineserver instances are running without WINEESYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} +diff --git a/dlls/ntdll/unix/esync.h b/dlls/ntdll/unix/esync.h +new file mode 100644 +index 00000000000..188304f3be7 +--- /dev/null ++++ b/dlls/ntdll/unix/esync.h +@@ -0,0 +1,61 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void) DECLSPEC_HIDDEN; ++extern void esync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_pulse_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_reset_event( HANDLE handle ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_set_event( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ ++ ++/* We have to synchronize on the fd cache mutex so that our calls to receive_fd ++ * don't race with theirs. It looks weird, I know. ++ * ++ * If we weren't trying to avoid touching the code I'd rename the mutex to ++ * "server_fd_mutex" or something similar. */ ++extern pthread_mutex_t fd_cache_mutex; ++ ++extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 33859dabf41..19b73256ef2 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -86,6 +86,7 @@ + #include "winioctl.h" + #include "winternl.h" + #include "unix_private.h" ++#include "esync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1564,6 +1565,7 @@ static void start_main_thread(void) + dbg_init(); + server_init_process(); + startup_info_size = server_init_thread( teb->Peb, &suspend ); ++ esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); + init_files(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index e12ed3a668a..34d4c80eee5 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -94,6 +94,7 @@ + #include "wine/server.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -134,7 +135,7 @@ timeout_t server_start_time = 0; /* time of server startup */ + sigset_t server_block_set; /* signals to block during server calls */ + static int fd_socket = -1; /* socket to exchange file descriptors with the server */ + static pid_t server_pid; +-static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER; + + /* atomically exchange a 64-bit value */ + static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val ) +@@ -843,7 +844,7 @@ void CDECL wine_server_send_fd( int fd ) + * + * Receive a file descriptor passed from the server. + */ +-static int receive_fd( obj_handle_t *handle ) ++int receive_fd( obj_handle_t *handle ) + { + struct iovec vec; + struct msghdr msghdr; +@@ -1697,6 +1698,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + NTSTATUS ret; + int fd = remove_fd_from_cache( handle ); + ++ if (do_esync()) ++ esync_close( handle ); ++ + SERVER_START_REQ( close_handle ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index bba7af7e34f..051c672bd12 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -72,6 +72,7 @@ + #include "wine/exception.h" + #include "wine/debug.h" + #include "unix_private.h" ++#include "esync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -322,6 +323,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_esync()) ++ return esync_create_semaphore( handle, access, attr, initial, max ); ++ + SERVER_START_REQ( create_semaphore ) + { + req->access = access; +@@ -345,6 +349,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_open_semaphore( handle, access, attr ); ++ + if ((ret = validate_open_object_attributes( attr ))) return ret; + + SERVER_START_REQ( open_semaphore ) +@@ -381,6 +388,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_semaphore( handle, info, ret_len ); ++ + SERVER_START_REQ( query_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -403,6 +413,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_release_semaphore( handle, count, previous ); ++ + SERVER_START_REQ( release_semaphore ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -427,6 +440,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + data_size_t len; + struct object_attributes *objattr; + ++ if (do_esync()) ++ return esync_create_event( handle, access, attr, type, state ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_event ) +@@ -454,6 +470,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_event( handle, access, attr ); ++ + SERVER_START_REQ( open_event ) + { + req->access = access; +@@ -476,6 +495,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_set_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -495,6 +517,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_reset_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -524,6 +549,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_pulse_event( handle ); ++ + SERVER_START_REQ( event_op ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -556,6 +584,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_event( handle, info, ret_len ); ++ + SERVER_START_REQ( query_event ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -581,6 +612,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + data_size_t len; + struct object_attributes *objattr; + ++ if (do_esync()) ++ return esync_create_mutex( handle, access, attr, owned ); ++ + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_mutex ) +@@ -607,6 +641,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_esync()) ++ return esync_open_mutex( handle, access, attr ); ++ + SERVER_START_REQ( open_mutex ) + { + req->access = access; +@@ -629,6 +666,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + NTSTATUS ret; + ++ if (do_esync()) ++ return esync_release_mutex( handle, prev_count ); ++ + SERVER_START_REQ( release_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -659,6 +699,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_esync()) ++ return esync_query_mutex( handle, info, ret_len ); ++ + SERVER_START_REQ( query_mutex ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1266,6 +1309,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (alertable) flags |= SELECT_ALERTABLE; + select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; + for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); +@@ -1291,6 +1341,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_esync()) ++ return esync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (!signal) return STATUS_INVALID_HANDLE; + + if (alertable) flags |= SELECT_ALERTABLE; +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 1ff56632496..e7033932bcb 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -58,6 +58,7 @@ struct ntdll_thread_data + struct list entry; /* entry in TEB list */ + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ ++ int esync_apc_fd; /* fd to wait on for user APCs */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index cac8ce4d368..bd89649e769 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2682,6 +2682,7 @@ static void init_teb( TEB *teb, PEB *peb ) + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; + thread_data->wait_fd[1] = -1; ++ thread_data->esync_apc_fd = -1; + list_add_head( &teb_list, &thread_data->entry ); + } + +diff --git a/dlls/rpcrt4/rpc_server.c b/dlls/rpcrt4/rpc_server.c +index 12260b7298b..a7cad5e273f 100644 +--- a/dlls/rpcrt4/rpc_server.c ++++ b/dlls/rpcrt4/rpc_server.c +@@ -699,10 +699,6 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) + } + LeaveCriticalSection(&cps->cs); + +- EnterCriticalSection(&listen_cs); +- CloseHandle(cps->server_thread); +- cps->server_thread = NULL; +- LeaveCriticalSection(&listen_cs); + TRACE("done\n"); + return 0; + } +@@ -1570,7 +1566,10 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + LIST_FOR_EACH_ENTRY(protseq, &protseqs, RpcServerProtseq, entry) + { + if ((wait_thread = protseq->server_thread)) ++ { ++ protseq->server_thread = NULL; + break; ++ } + } + LeaveCriticalSection(&server_cs); + if (!wait_thread) +@@ -1579,6 +1578,7 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void ) + TRACE("waiting for thread %u\n", GetThreadId(wait_thread)); + LeaveCriticalSection(&listen_cs); + WaitForSingleObject(wait_thread, INFINITE); ++ CloseHandle(wait_thread); + EnterCriticalSection(&listen_cs); + } + if (listen_done_event == event) +diff --git a/include/config.h.in b/include/config.h.in +index 6ca4e1bc8c8..f315921dee8 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -699,6 +699,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_PORT_H + ++/* Define to 1 if you have the `ppoll' function. */ ++#undef HAVE_PPOLL ++ + /* Define to 1 if you have the `prctl' function. */ + #undef HAVE_PRCTL + +@@ -822,6 +825,9 @@ + /* Define to 1 if `interface_id' is a member of `sg_io_hdr_t'. */ + #undef HAVE_SG_IO_HDR_T_INTERFACE_ID + ++/* Define to 1 if you have the `shm_open' function. */ ++#undef HAVE_SHM_OPEN ++ + /* Define if sigaddset is supported */ + #undef HAVE_SIGADDSET + +@@ -1022,6 +1028,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EPOLL_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_EVENTFD_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_EVENT_H + +diff --git a/server/Makefile.in b/server/Makefile.in +index 9a695cefc30..8bd612b4728 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -11,6 +11,7 @@ C_SRCS = \ + debugger.c \ + device.c \ + directory.c \ ++ esync.c \ + event.c \ + fd.c \ + file.c \ +diff --git a/server/async.c b/server/async.c +index 24fed811da2..1b4f86a1b8b 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -70,6 +70,7 @@ static const struct object_ops async_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -484,6 +485,7 @@ static const struct object_ops iosb_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index b61fa276661..73d858fef82 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -80,6 +80,7 @@ static const struct object_ops atom_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 2cccc5d7a88..85afb0cbdc5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -115,6 +115,7 @@ static const struct object_ops dir_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index e4546832620..54a5fb683cc 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -77,6 +77,7 @@ static const struct object_ops clipboard_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index c9b6995e0b2..ef66260c991 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -64,6 +64,7 @@ static const struct object_ops completion_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index d1e3cae8919..78635f3fae1 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -42,6 +42,7 @@ + #include "wincon.h" + #include "winternl.h" + #include "wine/condrv.h" ++#include "esync.h" + + struct screen_buffer; + +@@ -80,6 +81,7 @@ static const struct object_ops console_input_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -133,6 +135,7 @@ struct console_server + int busy; /* flag if server processing an ioctl */ + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -142,6 +145,7 @@ static struct fd *console_server_get_fd( struct object *obj ); + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); + + static const struct object_ops console_server_ops = + { +@@ -151,6 +155,7 @@ static const struct object_ops console_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ ++ console_server_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops screen_buffer_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -265,6 +271,7 @@ static const struct object_ops console_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -303,6 +310,7 @@ static const struct object_ops console_connection_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -759,6 +767,13 @@ static int console_server_signaled( struct object *obj, struct wait_queue_entry + return !server->console || !list_empty( &server->queue ); + } + ++static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return server->esync_fd; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -783,6 +798,7 @@ static struct object *create_console_server( void ) + list_init( &server->queue ); + list_init( &server->read_queue ); + server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT ); ++ server->esync_fd = -1; + if (!server->fd) + { + release_object( server ); +@@ -790,6 +806,9 @@ static struct object *create_console_server( void ) + } + allow_fd_caching(server->fd); + ++ if (do_esync()) ++ server->esync_fd = esync_create_fd( 0, 0 ); ++ + return &server->obj; + } + +diff --git a/server/debugger.c b/server/debugger.c +index e4a6c1e43a8..c37f97aa0b6 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -73,6 +73,7 @@ static const struct object_ops debug_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +102,7 @@ static const struct object_ops debug_ctx_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + debug_ctx_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index 652da83e1e2..b8ce131c732 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -39,6 +39,7 @@ + #include "handle.h" + #include "request.h" + #include "process.h" ++#include "esync.h" + + /* IRP object */ + +@@ -68,6 +69,7 @@ static const struct object_ops irp_call_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + irp_call_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -94,10 +96,12 @@ struct device_manager + struct list requests; /* list of pending irps across all devices */ + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -108,6 +112,7 @@ static const struct object_ops device_manager_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ ++ device_manager_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -152,6 +157,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -203,6 +209,7 @@ static const struct object_ops device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -747,6 +754,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ esync_clear( file->device->manager->esync_fd ); ++ + list_remove( &irp->mgr_entry ); + set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 ); + } +@@ -782,6 +792,13 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry + return !list_empty( &manager->requests ); + } + ++static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return manager->esync_fd; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -816,6 +833,9 @@ static void device_manager_destroy( struct object *obj ) + assert( !irp->file && !irp->async ); + release_object( irp ); + } ++ ++ if (do_esync()) ++ close( manager->esync_fd ); + } + + static struct device_manager *create_device_manager(void) +@@ -828,6 +848,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->devices ); + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); ++ ++ if (do_esync()) ++ manager->esync_fd = esync_create_fd( 0, 0 ); + } + return manager; + } +@@ -993,6 +1016,9 @@ DECL_HANDLER(get_next_device_request) + /* we already own the object if it's only on manager queue */ + if (irp->file) grab_object( irp ); + manager->current_call = irp; ++ ++ if (do_esync() && list_empty( &manager->requests )) ++ esync_clear( manager->esync_fd ); + } + else close_handle( current->process, reply->next ); + } +diff --git a/server/directory.c b/server/directory.c +index 9e3fef1177e..007ec1002ac 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -58,6 +58,7 @@ static const struct object_ops object_type_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,6 +98,7 @@ static const struct object_ops directory_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +new file mode 100644 +index 00000000000..5c641fdbe01 +--- /dev/null ++++ b/server/esync.c +@@ -0,0 +1,590 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++#include "wine/port.h" ++ ++#include ++#include ++#include ++#ifdef HAVE_SYS_EVENTFD_H ++# include ++#endif ++#ifdef HAVE_SYS_MMAN_H ++# include ++#endif ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "file.h" ++#include "esync.h" ++ ++int do_esync(void) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ static int do_esync_cached = -1; ++ ++ if (do_esync_cached == -1) ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ ++ return do_esync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void esync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino ); ++ ++ shm_unlink( shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ fprintf( stderr, "esync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct esync ++{ ++ struct object obj; /* object header */ ++ int fd; /* eventfd file descriptor */ ++ enum esync_type type; ++ unsigned int shm_idx; /* index into the shared memory section */ ++ struct list mutex_entry; /* entry in the mutex list (if applicable) */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ); ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int esync_map_access( struct object *obj, unsigned int access ); ++static void esync_destroy( struct object *obj ); ++ ++const struct object_ops esync_ops = ++{ ++ sizeof(struct esync), /* size */ ++ esync_dump, /* dump */ ++ no_get_type, /* get_type */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ esync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ esync_destroy /* destroy */ ++}; ++ ++static void esync_dump( struct object *obj, int verbose ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ assert( obj->ops == &esync_ops ); ++ fprintf( stderr, "esync fd=%d\n", esync->fd ); ++} ++ ++static int esync_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ *type = esync->type; ++ return esync->fd; ++} ++ ++static unsigned int esync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void esync_destroy( struct object *obj ) ++{ ++ struct esync *esync = (struct esync *)obj; ++ if (esync->type == ESYNC_MUTEX) ++ list_remove( &esync->mutex_entry ); ++ close( esync->fd ); ++} ++ ++static int type_matches( enum esync_type type1, enum esync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == ESYNC_AUTO_EVENT || type1 == ESYNC_MANUAL_EVENT) && ++ (type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT)); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++struct semaphore ++{ ++ int max; ++ int count; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct mutex ++{ ++ DWORD tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++struct event ++{ ++ int signaled; ++ int locked; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct esync *create_esync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int initval, int max, enum esync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ struct esync *esync; ++ ++ if ((esync = create_named_object( root, &esync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ int flags = EFD_CLOEXEC | EFD_NONBLOCK; ++ ++ if (type == ESYNC_SEMAPHORE) ++ flags |= EFD_SEMAPHORE; ++ ++ /* initialize it if it didn't already exist */ ++ esync->fd = eventfd( initval, flags ); ++ if (esync->fd == -1) ++ { ++ perror( "eventfd" ); ++ file_set_error(); ++ release_object( esync ); ++ return NULL; ++ } ++ esync->type = type; ++ ++ /* Use the fd as index, since that'll be unique across all ++ * processes, but should hopefully end up also allowing reuse. */ ++ esync->shm_idx = esync->fd + 1; /* we keep index 0 reserved */ ++ while (esync->shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "esync: couldn't expand %s to size %ld: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ switch (type) ++ { ++ case ESYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = get_shm( esync->shm_idx ); ++ semaphore->max = max; ++ semaphore->count = initval; ++ break; ++ } ++ case ESYNC_AUTO_EVENT: ++ case ESYNC_MANUAL_EVENT: ++ { ++ struct event *event = get_shm( esync->shm_idx ); ++ event->signaled = initval ? 1 : 0; ++ event->locked = 0; ++ break; ++ } ++ case ESYNC_MUTEX: ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ mutex->tid = initval ? 0 : current->id; ++ mutex->count = initval ? 0 : 1; ++ list_add_tail( &mutex_list, &esync->mutex_entry ); ++ break; ++ } ++ default: ++ assert( 0 ); ++ } ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, esync->type )) ++ { ++ release_object( &esync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ return esync; ++#else ++ /* FIXME: Provide a fallback implementation using pipe(). */ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++/* Create a file descriptor for an existing handle. ++ * Caller must close the handle when it's done; it's not linked to an esync ++ * server object in any way. */ ++int esync_create_fd( int initval, int flags ) ++{ ++#ifdef HAVE_SYS_EVENTFD_H ++ int fd; ++ ++ fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK ); ++ if (fd == -1) ++ perror( "eventfd" ); ++ ++ return fd; ++#else ++ return -1; ++#endif ++} ++ ++/* Wake up a specific fd. */ ++void esync_wake_fd( int fd ) ++{ ++ static const uint64_t value = 1; ++ ++ if (write( fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++} ++ ++/* Wake up a server-side esync object. */ ++void esync_wake_up( struct object *obj ) ++{ ++ enum esync_type dummy; ++ int fd; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &dummy ); ++ esync_wake_fd( fd ); ++ } ++} ++ ++void esync_clear( int fd ) ++{ ++ uint64_t value; ++ ++ /* we don't care about the return value */ ++ read( fd, &value, sizeof(value) ); ++} ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/* Server-side event support. */ ++void esync_set_event( struct esync *esync ) ++{ ++ static const uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_set_event() fd=%d\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ { ++ if (write( esync->fd, &value, sizeof(value) ) == -1) ++ perror( "esync: write" ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_reset_event( struct esync *esync ) ++{ ++ static uint64_t value = 1; ++ struct event *event = get_shm( esync->shm_idx ); ++ ++ assert( esync->obj.ops == &esync_ops ); ++ assert( event != NULL ); ++ ++ if (debug_level) ++ fprintf( stderr, "esync_reset_event() fd=%d\n", esync->fd ); ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Acquire the spinlock. */ ++ while (__sync_val_compare_and_swap( &event->locked, 0, 1 )) ++ small_pause(); ++ } ++ ++ /* Only bother signaling the fd if we weren't already signaled. */ ++ if (__atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST )) ++ { ++ /* we don't care about the return value */ ++ read( esync->fd, &value, sizeof(value) ); ++ } ++ ++ if (esync->type == ESYNC_MANUAL_EVENT) ++ { ++ /* Release the spinlock. */ ++ event->locked = 0; ++ } ++} ++ ++void esync_abandon_mutexes( struct thread *thread ) ++{ ++ struct esync *esync; ++ ++ LIST_FOR_EACH_ENTRY( esync, &mutex_list, struct esync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( esync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "esync_abandon_mutexes() fd=%d\n", esync->fd ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ esync_wake_fd( esync->fd ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_esync) ++{ ++ struct esync *esync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_esync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!req->type) ++ { ++ set_error( STATUS_INVALID_PARAMETER ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->max, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, esync, ++ req->access, objattr->attributes ); ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_esync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &esync_ops, &name, req->attributes ); ++ ++ /* send over the fd */ ++ if (reply->handle) ++ { ++ struct esync *esync; ++ ++ if (!(esync = (struct esync *)get_handle_obj( current->process, reply->handle, ++ 0, &esync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, esync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( esync ); ++ return; ++ } ++ ++ reply->type = esync->type; ++ reply->shm_idx = esync->shm_idx; ++ ++ send_client_fd( current->process, esync->fd, reply->handle ); ++ release_object( esync ); ++ } ++} ++ ++/* Retrieve a file descriptor for an esync object which will be signaled by the ++ * server. The client should only read from (i.e. wait on) this object. */ ++DECL_HANDLER(get_esync_fd) ++{ ++ struct object *obj; ++ enum esync_type type; ++ int fd; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_esync_fd) ++ { ++ fd = obj->ops->get_esync_fd( obj, &type ); ++ reply->type = type; ++ if (obj->ops == &esync_ops) ++ { ++ struct esync *esync = (struct esync *)obj; ++ reply->shm_idx = esync->shm_idx; ++ } ++ else ++ reply->shm_idx = 0; ++ send_client_fd( current->process, fd, req->handle ); ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: esync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++/* Return the fd used for waiting on user APCs. */ ++DECL_HANDLER(get_esync_apc_fd) ++{ ++ send_client_fd( current->process, current->esync_apc_fd, current->id ); ++} +diff --git a/server/esync.h b/server/esync.h +new file mode 100644 +index 00000000000..125da8e9d12 +--- /dev/null ++++ b/server/esync.h +@@ -0,0 +1,33 @@ ++/* ++ * eventfd-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_esync(void); ++void esync_init(void); ++int esync_create_fd( int initval, int flags ); ++void esync_wake_fd( int fd ); ++void esync_wake_up( struct object *obj ); ++void esync_clear( int fd ); ++ ++struct esync; ++ ++extern const struct object_ops esync_ops; ++void esync_set_event( struct esync *esync ); ++void esync_reset_event( struct esync *esync ); ++void esync_abandon_mutexes( struct thread *thread ); +diff --git a/server/event.c b/server/event.c +index 9d8af7c87ea..8607b494b6d 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -35,6 +35,7 @@ + #include "thread.h" + #include "request.h" + #include "security.h" ++#include "esync.h" + + struct event + { +@@ -42,15 +43,18 @@ struct event + struct list kernel_object; /* list of kernel object pointers */ + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void event_dump( struct object *obj, int verbose ); + static struct object_type *event_get_type( struct object *obj ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int event_map_access( struct object *obj, unsigned int access ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); ++static void event_destroy( struct object *obj ); + + static const struct object_ops event_ops = + { +@@ -60,6 +64,7 @@ static const struct object_ops event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ ++ event_get_esync_fd, /* get_esync_fd */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -73,7 +78,7 @@ static const struct object_ops event_ops = + no_open_file, /* open_file */ + event_get_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -95,6 +100,7 @@ static const struct object_ops keyed_event_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -126,6 +132,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + list_init( &event->kernel_object ); + event->manual_reset = manual_reset; + event->signaled = initial_state; ++ ++ if (do_esync()) ++ event->esync_fd = esync_create_fd( initial_state, 0 ); + } + } + return event; +@@ -133,6 +142,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { ++ struct object *obj; ++ if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + return (struct event *)get_handle_obj( process, handle, access, &event_ops ); + } + +@@ -146,6 +159,12 @@ void pulse_event( struct event *event ) + + void set_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_set_event( (struct esync *)event ); ++ return; ++ } ++ + event->signaled = 1; + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); +@@ -153,7 +172,15 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_esync() && event->obj.ops == &esync_ops) ++ { ++ esync_reset_event( (struct esync *)event ); ++ return; ++ } + event->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( event->esync_fd ); + } + + static void event_dump( struct object *obj, int verbose ) +@@ -177,6 +204,13 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry ) + return event->signaled; + } + ++static int event_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = event->manual_reset ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return event->esync_fd; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +@@ -214,6 +248,14 @@ static struct list *event_get_kernel_obj_list( struct object *obj ) + return &event->kernel_object; + } + ++static void event_destroy( struct object *obj ) ++{ ++ struct event *event = (struct event *)obj; ++ ++ if (do_esync()) ++ close( event->esync_fd ); ++} ++ + struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd ) + { +diff --git a/server/fd.c b/server/fd.c +index e5a4be7a3df..bbc2462163d 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -102,6 +102,7 @@ + #include "handle.h" + #include "process.h" + #include "request.h" ++#include "esync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -203,6 +204,7 @@ struct fd + struct completion *completion; /* completion object attached to this fd */ + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -216,6 +218,7 @@ static const struct object_ops fd_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -257,6 +260,7 @@ static const struct object_ops device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops inode_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -339,6 +344,7 @@ static const struct object_ops file_lock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1589,6 +1595,9 @@ static void fd_destroy( struct object *obj ) + free( fd->unlink_name ); + free( fd->unix_name ); + } ++ ++ if (do_esync()) ++ close( fd->esync_fd ); + } + + /* check if the desired access is possible without violating */ +@@ -1704,12 +1713,16 @@ static struct fd *alloc_fd_object(void) + fd->poll_index = -1; + fd->completion = NULL; + fd->comp_flags = 0; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1742,11 +1755,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->completion = NULL; + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; ++ fd->esync_fd = -1; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); ++ ++ if (do_esync()) ++ fd->esync_fd = esync_create_fd( 0, 0 ); + return fd; + } + +@@ -2172,6 +2189,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return; + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); ++ ++ if (do_esync() && !signaled) ++ esync_clear( fd->esync_fd ); + } + + /* handler for close_handle that refuses to close fd-associated handles in other processes */ +@@ -2203,6 +2223,15 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ) + return ret; + } + ++int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ int ret = fd->esync_fd; ++ *type = ESYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + /* default map_access() routine for objects that behave like an fd */ + unsigned int default_fd_map_access( struct object *obj, unsigned int access ) + { +diff --git a/server/file.c b/server/file.c +index 23b6de275cd..49c4ba32b42 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -111,6 +111,7 @@ static const struct object_ops file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index bcbdea5b8ce..cae0ac1e395 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -102,6 +102,7 @@ extern void set_fd_signaled( struct fd *fd, int signaled ); + extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); ++extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); + extern unsigned int default_fd_map_access( struct object *obj, unsigned int access ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); +diff --git a/server/handle.c b/server/handle.c +index c1fb4a490a9..cb5628b7e06 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -123,6 +123,7 @@ static const struct object_ops handle_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 2a3da247313..61b5014c442 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -81,6 +81,7 @@ static const struct object_ops hook_table_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index fb2a94cc7b8..18fef4b0466 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -78,6 +78,7 @@ static const struct object_ops mailslot_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -136,6 +137,7 @@ static const struct object_ops mail_writer_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -199,6 +201,7 @@ static const struct object_ops mailslot_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -229,6 +232,7 @@ static const struct object_ops mailslot_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 14ba74638ae..3e02cbb3832 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -36,6 +36,7 @@ + #include "file.h" + #include "thread.h" + #include "request.h" ++#include "esync.h" + + /* command-line options */ + int debug_level = 0; +@@ -140,6 +141,9 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_esync()) ++ esync_init(); ++ + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); + init_scheduler(); +diff --git a/server/mapping.c b/server/mapping.c +index 1da05412b6a..10def3ca694 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -68,6 +68,7 @@ static const struct object_ops ranges_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -104,6 +105,7 @@ static const struct object_ops shared_map_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +164,7 @@ static const struct object_ops mapping_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index b4f04ad307b..1235ab4731f 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -61,6 +61,7 @@ static const struct object_ops mutex_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index c83e8c17027..e59a5b6c183 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -118,6 +118,7 @@ static const struct object_ops named_pipe_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -162,6 +163,7 @@ static const struct object_ops pipe_server_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -205,6 +207,7 @@ static const struct object_ops pipe_client_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ default_fd_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -252,6 +255,7 @@ static const struct object_ops named_pipe_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -283,6 +287,7 @@ static const struct object_ops named_pipe_device_file_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 43f636b397b..5b6bb9cbfe1 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -68,6 +68,8 @@ struct object_ops + void (*remove_queue)(struct object *,struct wait_queue_entry *); + /* is object signaled? */ + int (*signaled)(struct object *,struct wait_queue_entry *); ++ /* return the esync fd for this object */ ++ int (*get_esync_fd)(struct object *, enum esync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index 1b6ddb1b982..df50955f621 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -49,6 +49,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + /* process structure */ + +@@ -68,6 +69,7 @@ static struct security_descriptor *process_get_sd( struct object *obj ); + static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -78,6 +80,7 @@ static const struct object_ops process_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ ++ process_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -129,6 +132,7 @@ static const struct object_ops startup_info_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -175,6 +179,7 @@ static const struct object_ops job_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -541,6 +546,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->trace_data = 0; + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; ++ process->esync_fd = -1; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -597,6 +603,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_esync()) ++ process->esync_fd = esync_create_fd( 0, 0 ); ++ + set_fd_events( process->msg_fd, POLLIN ); /* start listening to events */ + return process; + +@@ -645,6 +654,7 @@ static void process_destroy( struct object *obj ) + if (process->id) free_ptid( process->id ); + if (process->token) release_object( process->token ); + free( process->dir_cache ); ++ if (do_esync()) close( process->esync_fd ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -668,6 +678,13 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry + return !process->running_threads; + } + ++static int process_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return process->esync_fd; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; +diff --git a/server/process.h b/server/process.h +index 56092e5b1ac..eec69ddbcaf 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -98,6 +98,7 @@ struct process + const struct rawinput_device *rawinput_mouse; /* rawinput mouse device, if any */ + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ ++ int esync_fd; /* esync file descriptor (signaled on exit) */ + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 45f22624d5d..2a8662354f6 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3713,3 +3713,56 @@ struct handle_info + @REQ(resume_process) + obj_handle_t handle; /* process handle */ + @END ++ ++enum esync_type ++{ ++ ESYNC_SEMAPHORE = 1, ++ ESYNC_AUTO_EVENT, ++ ESYNC_MANUAL_EVENT, ++ ESYNC_MUTEX, ++ ESYNC_AUTO_SERVER, ++ ESYNC_MANUAL_SERVER, ++ ESYNC_QUEUE, ++}; ++ ++/* Create a new eventfd-based synchronization object */ ++@REQ(create_esync) ++ unsigned int access; /* wanted access rights */ ++ int initval; /* initial value */ ++ int type; /* type of esync object */ ++ int max; /* maximum count on a semaphore */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* actual type (may be different for events) */ ++ unsigned int shm_idx; ++@END ++ ++@REQ(open_esync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of esync object (above) */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of esync object (above) */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the esync fd for an object. */ ++@REQ(get_esync_fd) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++/* Notify the server that we are doing a message wait or done with one. */ ++@REQ(esync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++/* Retrieve the fd to wait on for user APCs. */ ++@REQ(get_esync_apc_fd) ++@END +diff --git a/server/queue.c b/server/queue.c +index 42b0f4e6bfd..a78748b96ca 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -43,6 +43,7 @@ + #include "process.h" + #include "request.h" + #include "user.h" ++#include "esync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -144,6 +145,8 @@ struct msg_queue + struct hook_table *hooks; /* hook table */ + timeout_t last_get_msg; /* time of last get message call */ + unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ ++ int esync_fd; /* esync file descriptor (signalled on message) */ ++ int esync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -160,6 +163,7 @@ static void msg_queue_dump( struct object *obj, int verbose ); + static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -175,6 +179,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_add_queue, /* add_queue */ + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ ++ msg_queue_get_esync_fd, /* get_esync_fd */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -212,6 +217,7 @@ static const struct object_ops thread_input_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -313,11 +319,16 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->hooks = NULL; + queue->last_get_msg = current_time; + queue->ignore_post_msg = 0; ++ queue->esync_fd = -1; ++ queue->esync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); ++ ++ if (do_esync()) ++ queue->esync_fd = esync_create_fd( 0, 0 ); + + thread->queue = queue; + } +@@ -497,6 +508,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + { + queue->wake_bits &= ~bits; + queue->changed_bits &= ~bits; ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + + /* check whether msg is a keyboard message */ +@@ -955,6 +969,10 @@ static int is_queue_hung( struct msg_queue *queue ) + if (get_wait_queue_thread(entry)->queue == queue) + return 0; /* thread is waiting on queue -> not hung */ + } ++ ++ if (do_esync() && queue->esync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + return 1; + } + +@@ -1010,6 +1028,13 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr + return ret || is_signaled( queue ); + } + ++static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = ESYNC_QUEUE; ++ return queue->esync_fd; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2491,6 +2516,9 @@ DECL_HANDLER(get_queue_status) + reply->wake_bits = queue->wake_bits; + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; ++ ++ if (do_esync() && !is_signaled( queue )) ++ esync_clear( queue->esync_fd ); + } + else reply->wake_bits = reply->changed_bits = 0; + } +@@ -3493,3 +3521,18 @@ DECL_HANDLER(get_rawinput_devices) + devices[i++] = e->device; + } + } ++ ++DECL_HANDLER(esync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->esync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index c00022ff63b..996bff5ef6d 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -167,6 +167,7 @@ static const struct object_ops key_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 97bf1a746d2..20b0ec309f3 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -96,6 +96,7 @@ static const struct object_ops master_socket_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index 1a1f796f85b..d7d3a24e48f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -58,6 +58,7 @@ static const struct object_ops semaphore_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index 30fe6e8380f..a50ace9903f 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -92,6 +92,7 @@ static const struct object_ops serial_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index 7c2bf2cc154..b6d6dcfc4b6 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -67,6 +67,7 @@ static const struct object_ops handler_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index 07e1cf3a2ca..c2dfa8fb8ce 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -172,6 +172,7 @@ static const struct object_ops sock_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + sock_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1173,6 +1174,7 @@ static const struct object_ops ifchange_ops = + add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1393,6 +1395,7 @@ static const struct object_ops socket_device_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 86ff09cd7e5..07f3c924f25 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -60,6 +60,7 @@ static const struct object_ops symlink_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 0e916e181bc..1a245c58396 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -51,6 +51,7 @@ + #include "request.h" + #include "user.h" + #include "security.h" ++#include "esync.h" + + + #ifdef __i386__ +@@ -110,6 +111,7 @@ static const struct object_ops thread_apc_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -147,6 +149,7 @@ static const struct object_ops context_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ ++ NULL, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -169,6 +172,7 @@ static const struct object_ops context_ops = + static void dump_thread( struct object *obj, int verbose ); + static struct object_type *thread_get_type( struct object *obj ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -182,6 +186,7 @@ static const struct object_ops thread_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ ++ thread_get_esync_fd, /* get_esync_fd */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -221,6 +226,8 @@ static inline void init_thread_structure( struct thread *thread ) + thread->context = NULL; + thread->teb = 0; + thread->entry_point = 0; ++ thread->esync_fd = -1; ++ thread->esync_apc_fd = -1; + thread->debug_ctx = NULL; + thread->system_regs = 0; + thread->queue = NULL; +@@ -357,6 +364,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_esync()) ++ { ++ thread->esync_fd = esync_create_fd( 0, 0 ); ++ thread->esync_apc_fd = esync_create_fd( 0, 0 ); ++ } ++ + set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ + add_process_thread( thread->process, thread ); + return thread; +@@ -437,6 +450,9 @@ static void destroy_thread( struct object *obj ) + if (thread->exit_poll) remove_timeout_user( thread->exit_poll ); + if (thread->id) free_ptid( thread->id ); + if (thread->token) release_object( thread->token ); ++ ++ if (do_esync()) ++ close( thread->esync_fd ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -461,6 +477,13 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ) + return mythread->state == TERMINATED && !mythread->exit_poll; + } + ++static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = ESYNC_MANUAL_SERVER; ++ return thread->esync_fd; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT; +@@ -1045,6 +1068,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_esync()) ++ esync_wake_up( obj ); ++ + LIST_FOR_EACH( ptr, &obj->wait_queue ) + { + struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); +@@ -1129,8 +1155,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + grab_object( apc ); + list_add_tail( queue, &apc->entry ); + if (!list_prev( queue, &apc->entry )) /* first one */ ++ { + wake_thread( thread ); + ++ if (do_esync() && queue == &thread->user_apc) ++ esync_wake_fd( thread->esync_apc_fd ); ++ } ++ + return 1; + } + +@@ -1176,6 +1207,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + apc = LIST_ENTRY( ptr, struct thread_apc, entry ); + list_remove( ptr ); + } ++ ++ if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ esync_clear( thread->esync_apc_fd ); ++ + return apc; + } + +@@ -1292,6 +1327,8 @@ void kill_thread( struct thread *thread, int violent_death ) + kill_console_processes( thread, 0 ); + debug_exit_thread( thread ); + abandon_mutexes( thread ); ++ if (do_esync()) ++ esync_abandon_mutexes( thread ); + if (violent_death) + { + send_thread_signal( thread, SIGQUIT ); +diff --git a/server/thread.h b/server/thread.h +index 78ca4c201b2..0f6108b684a 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -54,6 +54,8 @@ struct thread + struct process *process; + thread_id_t id; /* thread id */ + struct list mutex_list; /* list of currently owned mutexes */ ++ int esync_fd; /* esync file descriptor (signalled on exit) */ ++ int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ + struct debug_ctx *debug_ctx; /* debugger context if this thread is a debugger */ + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ +diff --git a/server/timer.c b/server/timer.c +index 9aba550fd93..dcbc9e2ece5 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -36,6 +36,7 @@ + #include "file.h" + #include "handle.h" + #include "request.h" ++#include "esync.h" + + struct timer + { +@@ -48,11 +49,13 @@ struct timer + struct thread *thread; /* thread that set the APC function */ + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ ++ int esync_fd; /* esync file descriptor */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static struct object_type *timer_get_type( struct object *obj ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static unsigned int timer_map_access( struct object *obj, unsigned int access ); + static void timer_destroy( struct object *obj ); +@@ -65,6 +68,7 @@ static const struct object_ops timer_ops = + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ ++ timer_get_esync_fd, /* get_esync_fd */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +103,10 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->period = 0; + timer->timeout = NULL; + timer->thread = NULL; ++ timer->esync_fd = -1; ++ ++ if (do_esync()) ++ timer->esync_fd = esync_create_fd( 0, 0 ); + } + } + return timer; +@@ -172,6 +180,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + { + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; ++ ++ if (do_esync()) ++ esync_clear( timer->esync_fd ); + } + timer->when = (expire <= 0) ? expire - monotonic_time : max( expire, current_time ); + timer->period = period; +@@ -205,6 +216,13 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ) + return timer->signaled; + } + ++static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER; ++ return timer->esync_fd; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 68cfcf234c1..0f128728b0f 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -147,6 +147,7 @@ static const struct object_ops token_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 883722eff36..1a031248a7c 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -64,6 +64,7 @@ static const struct object_ops winstation_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -89,6 +90,7 @@ static const struct object_ops desktop_ops = + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ ++ NULL, /* get_esync_fd */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ diff --git a/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging.patch b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging.patch index b68222d0c..d84455ee2 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/esync/legacy/esync-unix-staging.patch @@ -2283,37 +2283,37 @@ index 00000000000..188304f3be7 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + -+extern int do_esync(void) DECLSPEC_HIDDEN; -+extern void esync_init(void) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++extern int do_esync(void); ++extern void esync_init(void); ++extern NTSTATUS esync_close( HANDLE handle ); + +extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max); +extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ); + +extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ); +extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_pulse_event( HANDLE handle ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_reset_event( HANDLE handle ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_set_event( HANDLE handle ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_pulse_event( HANDLE handle ); ++extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_reset_event( HANDLE handle ); ++extern NTSTATUS esync_set_event( HANDLE handle ); + +extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ); +extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; -+extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ); ++extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ); + +extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); +extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, -+ const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ const LARGE_INTEGER *timeout ); + + +/* We have to synchronize on the fd cache mutex so that our calls to receive_fd @@ -2323,7 +2323,7 @@ index 00000000000..188304f3be7 + * "server_fd_mutex" or something similar. */ +extern pthread_mutex_t fd_cache_mutex; + -+extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN; ++extern int receive_fd( obj_handle_t *handle ); diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 33859dabf41..19b73256ef2 100644 --- a/dlls/ntdll/unix/loader.c diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync index d26868dca..79b9bb4e4 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync @@ -4,8 +4,12 @@ # fsync-unix - requares 5.20+ Wine version if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 0c249e6125fc9dc6ee86b4ef6ae0d9fa2fc6291b HEAD ); then if [ "$_staging_esync" = "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD ); then _patchname='fsync-unix-staging.patch' && _patchmsg="Applied fsync patches (unix, staging)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor d5f23441ad460844f552d301680e061dd1a5e90a HEAD ); then + _patchname='fsync-unix-staging-c14de4c.patch' && _patchmsg="Applied fsync patches (unix, staging)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + _patchname='fsync-unix-staging-d5f2344.patch' && _patchmsg="Applied fsync patches (unix, staging)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 5b56bad50b85acb05244894990053f8b0de2e458 HEAD ); then _patchname='fsync-unix-staging-af4378d.patch' && _patchmsg="Applied fsync patches (unix, staging)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor e3001b6a7c087da5a680b412d82da878e0149e8b HEAD ); then @@ -45,8 +49,12 @@ fi fi elif [ "$_use_esync" = "true" ]; then - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor c14de4c85e79563f5e859765d0015892ae925cd6 HEAD ); then _patchname='fsync-unix-mainline.patch' && _patchmsg="Applied fsync patches (unix, mainline)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor d5f23441ad460844f552d301680e061dd1a5e90a HEAD ); then + _patchname='fsync-unix-mainline-c14de4c.patch' && _patchmsg="Applied fsync patches (unix, mainline)" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor af4378d46dbb72e682017485212442bf865c2226 HEAD ); then + _patchname='fsync-unix-mainline-d5f2344.patch' && _patchmsg="Applied fsync patches (unix, mainline)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 5b56bad50b85acb05244894990053f8b0de2e458 HEAD ); then _patchname='fsync-unix-mainline-af4378d.patch' && _patchmsg="Applied fsync patches (unix, mainline)" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor e3001b6a7c087da5a680b412d82da878e0149e8b HEAD ); then @@ -88,8 +96,14 @@ echo "Keep Fsync legacy version in this build" >> "$_where"/last_build_config.log fi else - if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 69910d38511ed69804fdb327bed4296bc1829276 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor bae3769ef9db585e765ff0cf62bca1533c4d1cc0 HEAD ); then _patchname='fsync_futex_waitv.patch' && _patchmsg="Applied patches for fsync to support futex_waitv" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then + _patchname='fsync_futex_waitv-bae3769.patch' && _patchmsg="Applied patches for fsync to support futex_waitv" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor d5f23441ad460844f552d301680e061dd1a5e90a HEAD ); then + _patchname='fsync_futex_waitv-59485f0.patch' && _patchmsg="Applied patches for fsync to support futex_waitv" && nonuser_patcher + elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 69910d38511ed69804fdb327bed4296bc1829276 HEAD ); then + _patchname='fsync_futex_waitv-d5f2344.patch' && _patchmsg="Applied patches for fsync to support futex_waitv" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 5b56bad50b85acb05244894990053f8b0de2e458 HEAD ); then _patchname='fsync_futex_waitv-69910d3.patch' && _patchmsg="Applied patches for fsync to support futex_waitv" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 0c249e6125fc9dc6ee86b4ef6ae0d9fa2fc6291b HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-mainline.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-mainline.patch index da0f43147..93f5b2120 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-mainline.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-mainline.patch @@ -60,7 +60,7 @@ new file mode 100644 index 00000000000..5012425b95a --- /dev/null +++ b/dlls/ntdll/unix/fsync.c -@@ -0,0 +1,1267 @@ +@@ -0,0 +1,1266 @@ +/* + * futex-based synchronization objects + * @@ -105,7 +105,6 @@ index 00000000000..5012425b95a + +#include "ntstatus.h" +#define WIN32_NO_STATUS -+#define NONAMELESSUNION +#include "windef.h" +#include "winternl.h" +#include "wine/debug.h" @@ -1354,35 +1353,35 @@ index 00000000000..b3604548554 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + -+extern int do_fsync(void) DECLSPEC_HIDDEN; -+extern void fsync_init(void) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern NTSTATUS fsync_close( HANDLE handle ); + +extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max); ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ); +extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ); +extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ); +extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ); +extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ); +extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ); + +extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); +extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 19b73256ef2..d0d979248f8 100644 --- a/dlls/ntdll/unix/loader.c diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-staging.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-staging.patch index ef9fbfec5..51b6d5d7f 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-staging.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync-unix-staging.patch @@ -60,7 +60,7 @@ new file mode 100644 index 00000000000..5012425b95a --- /dev/null +++ b/dlls/ntdll/unix/fsync.c -@@ -0,0 +1,1267 @@ +@@ -0,0 +1,1266 @@ +/* + * futex-based synchronization objects + * @@ -105,7 +105,6 @@ index 00000000000..5012425b95a + +#include "ntstatus.h" +#define WIN32_NO_STATUS -+#define NONAMELESSUNION +#include "windef.h" +#include "winternl.h" +#include "wine/debug.h" @@ -1354,35 +1353,35 @@ index 00000000000..b3604548554 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + -+extern int do_fsync(void) DECLSPEC_HIDDEN; -+extern void fsync_init(void) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern NTSTATUS fsync_close( HANDLE handle ); + +extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max); ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ); +extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ); +extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ); +extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ); +extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ); +extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, -+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; -+extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ const OBJECT_ATTRIBUTES *attr ); ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ); ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ); + +extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); +extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, -+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ); diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 19b73256ef2..d0d979248f8 100644 --- a/dlls/ntdll/unix/loader.c diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync_futex_waitv.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync_futex_waitv.patch index 2f916f226..93c510170 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync_futex_waitv.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/fsync_futex_waitv.patch @@ -104,7 +104,7 @@ index d0f8be6c309..f9f6e252b6c 100644 + +static void async_file_complete_io( struct async_file_read_job *job, NTSTATUS status, ULONG total ) +{ -+ job->io->u.Status = status; ++ job->io->Status = status; + job->io->Information = total; + + if (job->event) NtSetEvent( job->event, NULL ); @@ -325,7 +325,7 @@ index d0f8be6c309..f9f6e252b6c 100644 TRACE( "%p %p\n", handle, io_status ); + if (ac_odyssey && !cancel_async_file_read( handle, NULL )) -+ return (io_status->u.Status = STATUS_SUCCESS); ++ return (io_status->Status = STATUS_SUCCESS); + SERVER_START_REQ( cancel_async ) { @@ -335,7 +335,7 @@ index d0f8be6c309..f9f6e252b6c 100644 TRACE( "%p %p %p\n", handle, io, io_status ); + if (ac_odyssey && !cancel_async_file_read( handle, io )) -+ return (io_status->u.Status = STATUS_SUCCESS); ++ return (io_status->Status = STATUS_SUCCESS); + SERVER_START_REQ( cancel_async ) { @@ -843,15 +843,15 @@ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index dbd3e645976..683c2a4f1c2 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h -@@ -146,6 +146,9 @@ extern BOOL is_wow64 DECLSPEC_HIDDEN; - extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; +@@ -146,6 +146,9 @@ #ifdef __i386__ + extern struct ldt_copy __wine_ldt_copy; #endif -+extern BOOL ac_odyssey DECLSPEC_HIDDEN; -+extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++extern BOOL ac_odyssey; ++extern BOOL fsync_simulate_sched_quantum; + - extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; - extern void init_startup_info(void) DECLSPEC_HIDDEN; + extern void init_environment(void); + extern void init_startup_info(void); extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER_PROCESS_PARAMETERS *params, diff --git a/include/config.h.in b/include/config.h.in index 8f9b9737b24..4670e4bc881 100644 diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-c14de4c.patch new file mode 100644 index 000000000..f8d09ab25 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-c14de4c.patch @@ -0,0 +1,3714 @@ +From 1d3dadfd7707ac176b67b50a0dc573f799778836 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:53:31 +0200 +Subject: Fsync rebased 5.13+ mainline + + +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index c96a62ae006..a9be561d87a 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -48,6 +48,7 @@ C_SRCS = \ + unix/env.c \ + unix/esync.c \ + unix/file.c \ ++ unix/fsync.c \ + unix/loader.c \ + unix/process.c \ + unix/registry.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index ed801c71991..ac0326b1aba 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -57,6 +57,7 @@ + + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(esync); + +@@ -66,7 +67,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + return ret; + } + +- if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) + msgwait = TRUE; + + if (has_esync && has_server) +@@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + } + } + +- if (wait_any || count == 1) ++ if (wait_any || count <= 1) + { + /* Try to check objects now, so we can obviate poll() at least. */ + for (i = 0; i < count; i++) +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +new file mode 100644 +index 00000000000..5012425b95a +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.c +@@ -0,0 +1,1266 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++#include "wine/debug.h" ++#include "wine/server.h" ++ ++#include "unix_private.h" ++#include "fsync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(fsync); ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++ int bitset; ++}; ++#include "poppack.h" ++ ++static inline void small_pause(void) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++} ++ ++static unsigned int spincount = 100; ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ if (getenv("WINEFSYNC_SPINCOUNT")) ++ spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); ++ } ++ ++ return do_fsync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("futexes not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct fsync ++{ ++ enum fsync_type type; ++ void *shm; /* pointer to shm section */ ++}; ++ ++struct semaphore ++{ ++ int count; ++ int max; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct event ++{ ++ int signaled; ++ int unused; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) ++#define FSYNC_LIST_ENTRIES 256 ++ ++static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; ++static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / FSYNC_LIST_BLOCK_SIZE; ++ return idx % FSYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fsync_list[0] = fsync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ fsync_list[entry] = ptr; ++ } ++ } ++ ++ if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) ++ fsync_list[entry][idx].shm = shm; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++static struct fsync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; ++ if (!fsync_list[entry][idx].type) return NULL; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper fsync object (i.e. an event, ++ * semaphore, etc. created using create_fsync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ unsigned int shm_idx = 0; ++ enum fsync_type type; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ SERVER_START_REQ( get_fsync_idx ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); ++ ++ *obj = add_to_list( handle, type, get_shm( shm_idx ) ); ++ return ret; ++} ++ ++NTSTATUS fsync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) ++ { ++ if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) ++ return STATUS_SUCCESS; ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ unsigned int shm_idx; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ SERVER_START_REQ( create_fsync ) ++ { ++ req->access = access; ++ req->low = low; ++ req->high = high; ++ req->type = type; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx )); ++ TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ unsigned int shm_idx; ++ ++ SERVER_START_REQ( open_fsync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx ) ); ++ ++ TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); ++ } ++ return ret; ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_fsync()) ++ { ++ /* make sure the server isn't running with WINEFSYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEFSYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} ++ ++NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); ++ ++ if (prev) *prev = current; ++ ++ futex_wake( &semaphore->count, INT_MAX ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); ++} ++ ++NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); ++} ++ ++NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ out->EventState = event->signaled; ++ out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_fsync( FSYNC_MUTEX, handle, access, attr, ++ initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); ++} ++ ++NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) ++{ ++ struct mutex *mutex; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ if (!--mutex->count) ++ { ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) ++{ ++ int ret; ++ ++ if (alertable) ++ { ++ int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; ++ struct futex_wait_block futexes[2]; ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ ++ futexes[0].addr = addr; ++ futexes[0].val = val; ++ futexes[1].addr = apc_futex; ++ futexes[1].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[0].pad = futexes[1].pad = 0; ++#endif ++ futexes[0].bitset = futexes[1].bitset = ~0; ++ ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait_multiple( futexes, 2, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, 2, NULL ); ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ } ++ else ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait( addr, val, &tmo_p ); ++ } ++ else ++ ret = futex_wait( addr, val, NULL ); ++ } ++ ++ if (!ret) ++ return 0; ++ else if (ret < 0 && errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero = {0}; ++ ++ struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ int has_fsync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ int dummy_futex = 0; ++ unsigned int spin; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD waitcount; ++ ULONGLONG end; ++ int i, ret; ++ ++ /* Grab the APC futex if we don't already have it. */ ++ if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) ++ { ++ unsigned int idx = 0; ++ SERVER_START_REQ( get_fsync_apc_idx ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ idx = reply->shm_idx; ++ } ++ SERVER_END_REQ; ++ ++ if (idx) ++ { ++ struct event *apc_event = get_shm( idx ); ++ ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; ++ } ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart > 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_fsync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_fsync && has_server) ++ FIXME("Can't wait on fsync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(fsync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); ++ } ++ } ++ ++ if (wait_any || count <= 1) ++ { ++ while (1) ++ { ++ /* Try to grab anything. */ ++ ++ if (alertable) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) ++ goto userapc; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj) ++ { ++ if (!obj->type) /* gcc complains if we put this in the switch */ ++ { ++ /* Someone probably closed an object while waiting on it. */ ++ WARN("Handle %p has type 0; was it closed?\n", handles[i]); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ int current; ++ ++ /* It would be a little clearer (and less error-prone) ++ * to use a dedicated interlocked_dec_if_nonzero() ++ * helper, but nesting loops like that is probably not ++ * great for performance... */ ++ for (spin = 0; spin <= spincount || current; ++spin) ++ { ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &semaphore->count; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &mutex->tid; ++ futexes[i].val = tid; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MANUAL_EVENT: ++ case FSYNC_MANUAL_SERVER: ++ case FSYNC_QUEUE: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ default: ++ ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); ++ assert(0); ++ } ++ } ++ else ++ { ++ /* Avoid breaking things entirely. */ ++ futexes[i].addr = &dummy_futex; ++ futexes[i].val = dummy_futex; ++ } ++ ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ } ++ ++ if (alertable) ++ { ++ /* We already checked if it was signaled; don't bother doing it again. */ ++ futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; ++ futexes[i].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ i++; ++ } ++ waitcount = i; ++ ++ /* Looks like everything is contended, so wait. */ ++ ++ if (timeout && !timeout->QuadPart) ++ { ++ /* Unlike esync, we already know that we've timed out, so we ++ * can avoid a syscall. */ ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else if (timeout) ++ { ++ LONGLONG timeleft = update_timeout( end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ++ ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ++ /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, ++ * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to ++ * try again, bad address is already handled by the fact that we ++ * tried to read from it, so only break out on a timeout. */ ++ if (ret == -1 && errno == ETIMEDOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ } /* while (1) */ ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ NTSTATUS status = STATUS_SUCCESS; ++ int current; ++ ++ while (1) ++ { ++ BOOL abandoned; ++ ++tryagain: ++ abandoned = FALSE; ++ ++ /* First step: try to wait on each object in sequence. */ ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ ++ while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) ++ { ++ status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ else if (obj) ++ { ++ /* this works for semaphores too */ ++ struct event *event = obj->shm; ++ ++ while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ ++ if (status == STATUS_TIMEOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return status; ++ } ++ else if (status == STATUS_USER_APC) ++ goto userapc; ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ ++ if (tid && tid != ~0 && tid != GetCurrentThreadId()) ++ goto tryagain; ++ } ++ else if (obj) ++ { ++ struct event *event = obj->shm; ++ ++ if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ goto tryagain; ++ } ++ } ++ ++ /* Yep, still signaled. Now quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ if (tid == GetCurrentThreadId()) ++ break; ++ if (tid && tid != ~0) ++ goto tooslow; ++ if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) ++ goto tooslow; ++ if (tid == ~0) ++ abandoned = TRUE; ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ goto tooslow; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ goto tooslow; ++ break; ++ } ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. ++ * Make sure to let ourselves know that we grabbed the mutexes. */ ++ for (i = 0; i < count; i++) ++ { ++ if (objs[i] && objs[i]->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = objs[i]->shm; ++ mutex->count++; ++ } ++ } ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ ++tooslow: ++ for (--i; i >= 0; i--) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ /* HACK: This won't do the right thing with abandoned ++ * mutexes, but fixing it is probably more trouble than ++ * it's worth. */ ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ __sync_fetch_and_add( &semaphore->count, 1 ); ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ default: ++ /* doesn't need to be put back */ ++ break; ++ } ++ } ++ } /* while (1) */ ++ } /* else (wait-all) */ ++ ++ assert(0); /* shouldn't reach here... */ ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_wait(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* Like esync, we need to let the server know when we are doing a message wait, ++ * and when we are done with one, so that all of the code surrounding hung ++ * queues works, and we also need this for WaitForInputIdle(). ++ * ++ * Unlike esync, we can't wait on the queue fd itself locally. Instead we let ++ * the server do that for us, the way it normally does. This could actually ++ * work for esync too, and that might be better. */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( fsync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from fsync_wait_objects(). */ ++NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ ret = fsync_release_semaphore( signal, 1, NULL ); ++ break; ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_MANUAL_EVENT: ++ ret = fsync_set_event( signal, NULL ); ++ break; ++ case FSYNC_MUTEX: ++ ret = fsync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} +diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h +new file mode 100644 +index 00000000000..b3604548554 +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.h +@@ -0,0 +1,49 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void) DECLSPEC_HIDDEN; ++extern void fsync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 19b73256ef2..d0d979248f8 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -87,6 +87,7 @@ + #include "winternl.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1565,6 +1566,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ fsync_init(); + esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 34d4c80eee5..c48a6339fee 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -95,6 +95,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -1698,6 +1699,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + NTSTATUS ret; + int fd = remove_fd_from_cache( handle ); + ++ if (do_fsync()) ++ fsync_close( handle ); ++ + if (do_esync()) + esync_close( handle ); + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 051c672bd12..d8a8eb526a1 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -73,6 +73,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -323,6 +324,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_fsync()) ++ return fsync_create_semaphore( handle, access, attr, initial, max ); ++ + if (do_esync()) + return esync_create_semaphore( handle, access, attr, initial, max ); + +@@ -349,6 +353,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_open_semaphore( handle, access, attr ); ++ + if (do_esync()) + return esync_open_semaphore( handle, access, attr ); + +@@ -388,6 +395,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_semaphore( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + +@@ -413,6 +423,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_semaphore( handle, count, previous ); ++ + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + +@@ -440,6 +453,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + ++ if (do_fsync()) ++ return fsync_create_event( handle, access, attr, type, state ); ++ + if (do_esync()) + return esync_create_event( handle, access, attr, type, state ); + +@@ -470,6 +486,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_event( handle, access, attr ); ++ + if (do_esync()) + return esync_open_event( handle, access, attr ); + +@@ -495,6 +514,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_set_event( handle, prev_state ); ++ + if (do_esync()) + return esync_set_event( handle ); + +@@ -517,6 +539,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_reset_event( handle, prev_state ); ++ + if (do_esync()) + return esync_reset_event( handle ); + +@@ -549,6 +574,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_pulse_event( handle, prev_state ); ++ + if (do_esync()) + return esync_pulse_event( handle ); + +@@ -584,6 +612,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_event( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + +@@ -612,6 +643,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_create_mutex( handle, access, attr, owned ); ++ + if (do_esync()) + return esync_create_mutex( handle, access, attr, owned ); + +@@ -641,6 +675,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_mutex( handle, access, attr ); ++ + if (do_esync()) + return esync_open_mutex( handle, access, attr ); + +@@ -666,6 +703,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_mutex( handle, prev_count ); ++ + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + +@@ -699,6 +739,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_mutex( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + +@@ -1309,6 +1352,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (do_esync()) + { + NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1341,6 +1391,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_fsync()) ++ return fsync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (do_esync()) + return esync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1374,7 +1427,24 @@ NTSTATUS WINAPI NtYieldExecution(void) + NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) + { + /* if alertable, we need to query the server */ +- if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ if (alertable) ++ { ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ } + + if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index e7033932bcb..a2f485d892f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -59,6 +59,7 @@ struct ntdll_thread_data + struct debug_info *debug_info; /* info for debugstr functions */ + void *start_stack; /* stack for thread startup */ + int esync_apc_fd; /* fd to wait on for user APCs */ ++ int *fsync_apc_futex; + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index bd89649e769..608c83b579c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2683,6 +2683,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data->esync_apc_fd = -1; ++ thread_data->fsync_apc_futex = NULL; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/server/Makefile.in b/server/Makefile.in +index 8bd612b4728..6dc5c465e52 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -15,6 +15,7 @@ C_SRCS = \ + event.c \ + fd.c \ + file.c \ ++ fsync.c \ + handle.c \ + hook.c \ + mach.c \ +diff --git a/server/async.c b/server/async.c +index 1b4f86a1b8b..ceff9c75c27 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -71,6 +71,7 @@ static const struct object_ops async_ops = + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index 73d858fef82..7acef97c30d 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 85afb0cbdc5..7eea72757c5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -116,6 +116,7 @@ static const struct object_ops dir_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index 54a5fb683cc..1fa61b67a88 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index ef66260c991..2149d0bbf32 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -65,6 +65,7 @@ static const struct object_ops completion_ops = + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index 78635f3fae1..824b930bba0 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -43,6 +43,7 @@ + #include "winternl.h" + #include "wine/condrv.h" + #include "esync.h" ++#include "fsync.h" + + struct screen_buffer; + +@@ -82,6 +83,7 @@ static const struct object_ops console_input_ops = + remove_queue, /* remove_queue */ + console_input_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -136,6 +138,7 @@ struct console_server + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -146,6 +149,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); + + static const struct object_ops console_server_ops = + { +@@ -156,6 +160,7 @@ static const struct object_ops console_server_ops = + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ + console_server_get_esync_fd, /* get_esync_fd */ ++ console_server_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -224,6 +229,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -272,6 +278,7 @@ static const struct object_ops console_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops input_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + input_device_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops output_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + output_device_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -587,8 +587,13 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ ++ if (do_fsync()) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync()) + esync_clear( server->esync_fd ); ++ + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -774,6 +787,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ + return server->esync_fd; + } + ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return server->fsync_idx; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -806,6 +828,9 @@ static struct object *create_console_server( void ) + } + allow_fd_caching(server->fd); + ++ if (do_fsync()) ++ server->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + server->esync_fd = esync_create_fd( 0, 0 ); + +@@ -1545,6 +1560,10 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + } +@@ -1645,6 +1664,10 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + +diff --git a/server/debugger.c b/server/debugger.c +index c37f97aa0b6..b7d88cfd2f2 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -103,6 +104,7 @@ static const struct object_ops debug_ctx_ops = + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index b8ce131c732..c56151c9b18 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -40,6 +40,7 @@ + #include "request.h" + #include "process.h" + #include "esync.h" ++#include "fsync.h" + + /* IRP object */ + +@@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,11 +99,13 @@ struct device_manager + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ + device_manager_get_esync_fd, /* get_esync_fd */ ++ device_manager_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -158,6 +163,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ fsync_clear( &file->device->manager->obj ); ++ + if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) + esync_clear( file->device->manager->esync_fd ); + +@@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ + return manager->esync_fd; + } + ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return manager->fsync_idx; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); + ++ if (do_fsync()) ++ manager->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + manager->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) + if (irp->file) grab_object( irp ); + manager->current_call = irp; + ++ if (do_fsync() && list_empty( &manager->requests )) ++ fsync_clear( &manager->obj ); ++ + if (do_esync() && list_empty( &manager->requests )) + esync_clear( manager->esync_fd ); + } +diff --git a/server/directory.c b/server/directory.c +index 007ec1002ac..62f2e50916f 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -59,6 +59,7 @@ static const struct object_ops object_type_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +100,7 @@ static const struct object_ops directory_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +index 5c641fdbe01..6395ba4378c 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "file.h" + #include "esync.h" ++#include "fsync.h" + + int do_esync(void) + { +@@ -51,7 +52,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -130,6 +131,7 @@ const struct object_ops esync_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/event.c b/server/event.c +index 8607b494b6d..28a52e61d00 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -36,6 +36,7 @@ + #include "request.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -44,6 +45,7 @@ struct event + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -51,6 +53,7 @@ static struct object_type *event_get_type( struct object *obj ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); + static void event_destroy( struct object *obj ); +@@ -65,6 +68,7 @@ static const struct object_ops event_ops = + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ + event_get_esync_fd, /* get_esync_fd */ ++ event_get_fsync_idx, /* get_fsync_idx */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + event->manual_reset = manual_reset; + event->signaled = initial_state; + ++ if (do_fsync()) ++ event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); ++ + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); + } +@@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { + struct object *obj; ++ ++ if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) + return (struct event *)obj; /* even though it's not an event */ + +@@ -155,10 +167,19 @@ void pulse_event( struct event *event ) + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); + event->signaled = 0; ++ ++ if (do_fsync()) ++ fsync_clear( &event->obj ); + } + + void set_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_set_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_set_event( (struct esync *)event ); +@@ -172,6 +193,12 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_reset_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_reset_event( (struct esync *)event ); +@@ -179,6 +206,9 @@ void reset_event( struct event *event ) + } + event->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &event->obj ); ++ + if (do_esync()) + esync_clear( event->esync_fd ); + } +@@ -211,6 +241,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) + return event->esync_fd; + } + ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return event->fsync_idx; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +diff --git a/server/fd.c b/server/fd.c +index bbc2462163d..c558229c3c8 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -103,6 +103,7 @@ + #include "process.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -205,6 +206,7 @@ struct fd + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -219,6 +221,7 @@ static const struct object_ops fd_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -261,6 +264,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -302,6 +306,7 @@ static const struct object_ops inode_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) + fd->completion = NULL; + fd->comp_flags = 0; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) + if (do_esync()) + fd->esync_fd = esync_create_fd( 1, 0 ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + fd->esync_fd = esync_create_fd( 0, 0 ); ++ + return fd; + } + +@@ -2190,6 +2205,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); + ++ if (do_fsync() && !signaled) ++ fsync_clear( fd->user ); ++ + if (do_esync() && !signaled) + esync_clear( fd->esync_fd ); + } +@@ -2232,6 +2250,15 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) + return ret; + } + ++unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ unsigned int ret = fd->fsync_idx; ++ *type = FSYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 49c4ba32b42..829c8685d63 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -112,6 +112,7 @@ static const struct object_ops file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index cae0ac1e395..ed030af8d8f 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -103,6 +103,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); ++extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/fsync.c b/server/fsync.c +new file mode 100644 +index 00000000000..0a88ecdd3f6 +--- /dev/null ++++ b/server/fsync.c +@@ -0,0 +1,524 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "fsync.h" ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++}; ++#include "poppack.h" ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ } ++ ++ return do_fsync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static int is_fsync_initialized; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if (!shm_unlink( shm_name )) ++ fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ is_fsync_initialized = 1; ++ ++ fprintf( stderr, "fsync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct fsync ++{ ++ struct object obj; ++ unsigned int shm_idx; ++ enum fsync_type type; ++ struct list mutex_entry; ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ); ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ); ++static void fsync_destroy( struct object *obj ); ++ ++const struct object_ops fsync_ops = ++{ ++ sizeof(struct fsync), /* size */ ++ &no_type, /* type */ ++ fsync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* get_esync_fd */ ++ fsync_get_fsync_idx, /* get_fsync_idx */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ fsync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ fsync_destroy /* destroy */ ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ assert( obj->ops == &fsync_ops ); ++ fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); ++} ++ ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ *type = fsync->type; ++ return fsync->shm_idx; ++} ++ ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void fsync_destroy( struct object *obj ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ if (fsync->type == FSYNC_MUTEX) ++ list_remove( &fsync->mutex_entry ); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++/* FIXME: This is rather inefficient... */ ++static unsigned int shm_idx_counter = 1; ++ ++unsigned int fsync_alloc_shm( int low, int high ) ++{ ++#ifdef __linux__ ++ int shm_idx; ++ int *shm; ++ ++ /* this is arguably a bit of a hack, but we need some way to prevent ++ * allocating shm for the master socket */ ++ if (!is_fsync_initialized) ++ return 0; ++ ++ shm_idx = shm_idx_counter++; ++ ++ while (shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ shm = get_shm( shm_idx ); ++ assert(shm); ++ shm[0] = low; ++ shm[1] = high; ++ ++ return shm_idx; ++#else ++ return 0; ++#endif ++} ++ ++static int type_matches( enum fsync_type type1, enum fsync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && ++ (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); ++} ++ ++struct fsync *create_fsync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int low, int high, enum fsync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef __linux__ ++ struct fsync *fsync; ++ ++ if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ /* initialize it if it didn't already exist */ ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ ++ fsync->shm_idx = fsync_alloc_shm( low, high ); ++ fsync->type = type; ++ if (type == FSYNC_MUTEX) ++ list_add_tail( &mutex_list, &fsync->mutex_entry ); ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, fsync->type )) ++ { ++ release_object( &fsync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ ++ return fsync; ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++/* shm layout for events or event-like objects. */ ++struct fsync_event ++{ ++ int signaled; ++ int unused; ++}; ++ ++void fsync_wake_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_wake_up( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_up: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_clear_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++void fsync_clear( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_set_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_reset_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++ ++void fsync_abandon_mutexes( struct thread *thread ) ++{ ++ struct fsync *fsync; ++ ++ LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( fsync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_fsync) ++{ ++ struct fsync *fsync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_fsync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, ++ req->high, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, fsync, ++ req->access, objattr->attributes ); ++ ++ reply->shm_idx = fsync->shm_idx; ++ reply->type = fsync->type; ++ release_object( fsync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_fsync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &fsync_ops, &name, req->attributes ); ++ ++ if (reply->handle) ++ { ++ struct fsync *fsync; ++ ++ if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, ++ 0, &fsync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, fsync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( fsync ); ++ return; ++ } ++ ++ reply->type = fsync->type; ++ reply->shm_idx = fsync->shm_idx; ++ release_object( fsync ); ++ } ++} ++ ++/* Retrieve the index of a shm section which will be signaled by the server. */ ++DECL_HANDLER(get_fsync_idx) ++{ ++ struct object *obj; ++ enum fsync_type type; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_fsync_idx) ++ { ++ reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); ++ reply->type = type; ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++DECL_HANDLER(get_fsync_apc_idx) ++{ ++ reply->shm_idx = current->fsync_apc_idx; ++} +diff --git a/server/fsync.h b/server/fsync.h +new file mode 100644 +index 00000000000..a91939b7f0a +--- /dev/null ++++ b/server/fsync.h +@@ -0,0 +1,34 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern unsigned int fsync_alloc_shm( int low, int high ); ++extern void fsync_wake_futex( unsigned int shm_idx ); ++extern void fsync_clear_futex( unsigned int shm_idx ); ++extern void fsync_wake_up( struct object *obj ); ++extern void fsync_clear( struct object *obj ); ++ ++struct fsync; ++ ++extern const struct object_ops fsync_ops; ++extern void fsync_set_event( struct fsync *fsync ); ++extern void fsync_reset_event( struct fsync *fsync ); ++extern void fsync_abandon_mutexes( struct thread *thread ); +diff --git a/server/handle.c b/server/handle.c +index cb5628b7e06..ff44446acc9 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 61b5014c442..379f9c074d5 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index 18fef4b0466..b541524c9a4 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 3e02cbb3832..7a09436b637 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -37,6 +37,7 @@ + #include "thread.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + /* command-line options */ + int debug_level = 0; +@@ -141,8 +142,14 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_fsync()) ++ fsync_init(); ++ + if (do_esync()) + esync_init(); ++ ++ if (!do_fsync() && !do_esync()) ++ fprintf( stderr, "wineserver: using server-side synchronization.\n" ); + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); +diff --git a/server/mapping.c b/server/mapping.c +index 10def3ca694..d55df5145f7 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index 1235ab4731f..ccdc383f0df 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index e59a5b6c183..d0ea32a24d5 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 5b6bb9cbfe1..4c92b174ef3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -70,6 +70,8 @@ struct object_ops + int (*signaled)(struct object *,struct wait_queue_entry *); + /* return the esync fd for this object */ + int (*get_esync_fd)(struct object *, enum esync_type *type); ++ /* return the fsync shm idx for this object */ ++ unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index df50955f621..c4f51dcad30 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -50,6 +50,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + /* process object */ + +@@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -81,6 +83,7 @@ static const struct object_ops process_ops = + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ + process_get_esync_fd, /* get_esync_fd */ ++ process_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -180,6 +184,7 @@ static const struct object_ops job_ops = + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -547,6 +552,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + process->esync_fd = -1; ++ process->fsync_idx = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -603,6 +609,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_fsync()) ++ process->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + process->esync_fd = esync_create_fd( 0, 0 ); + +@@ -685,6 +694,13 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) + return process->esync_fd; + } + ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return process->fsync_idx; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index eec69ddbcaf..abb4ed31b48 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -99,6 +99,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ ++ unsigned int fsync_idx; + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 2a8662354f6..381bd8d8218 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3766,3 +3766,57 @@ enum esync_type + /* Retrieve the fd to wait on for user APCs. */ + @REQ(get_esync_apc_fd) + @END ++ ++enum fsync_type ++{ ++ FSYNC_SEMAPHORE = 1, ++ FSYNC_AUTO_EVENT, ++ FSYNC_MANUAL_EVENT, ++ FSYNC_MUTEX, ++ FSYNC_AUTO_SERVER, ++ FSYNC_MANUAL_SERVER, ++ FSYNC_QUEUE, ++}; ++ ++/* Create a new futex-based synchronization object */ ++@REQ(create_fsync) ++ unsigned int access; /* wanted access rights */ ++ int low; /* initial value of low word */ ++ int high; /* initial value of high word */ ++ int type; /* type of fsync object */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Open an fsync object */ ++@REQ(open_fsync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of fsync object */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the shm index for an object. */ ++@REQ(get_fsync_idx) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++@REQ(fsync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++@REQ(get_fsync_apc_idx) ++@REPLY ++ unsigned int shm_idx; ++@END +diff --git a/server/queue.c b/server/queue.c +index a78748b96ca..dccf43197cf 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "user.h" + #include "esync.h" ++#include "fsync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -147,6 +148,8 @@ struct msg_queue + unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ + int esync_fd; /* esync file descriptor (signalled on message) */ + int esync_in_msgwait; /* our thread is currently waiting on us */ ++ unsigned int fsync_idx; ++ int fsync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -164,6 +167,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -180,6 +184,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ + msg_queue_get_esync_fd, /* get_esync_fd */ ++ msg_queue_get_fsync_idx, /* get_fsync_idx */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops thread_input_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -321,12 +327,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->ignore_post_msg = 0; + queue->esync_fd = -1; + queue->esync_in_msgwait = 0; ++ queue->fsync_idx = 0; ++ queue->fsync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); + ++ if (do_fsync()) ++ queue->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + queue->esync_fd = esync_create_fd( 0, 0 ); + +@@ -509,6 +520,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + queue->wake_bits &= ~bits; + queue->changed_bits &= ~bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -970,6 +984,9 @@ static int is_queue_hung( struct msg_queue *queue ) + return 0; /* thread is waiting on queue -> not hung */ + } + ++ if (do_fsync() && queue->fsync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + +@@ -1035,6 +1052,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) + return queue->esync_fd; + } + ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = FSYNC_QUEUE; ++ return queue->fsync_idx; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2517,6 +2541,9 @@ DECL_HANDLER(get_queue_status) + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -3536,3 +3563,18 @@ DECL_HANDLER(esync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fsync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->fsync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -168,6 +168,7 @@ static const struct object_ops key_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 20b0ec309f3..9fd08139375 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index d7d3a24e48f..604721d0d5f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index a50ace9903f..78d33460892 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -93,6 +93,7 @@ static const struct object_ops serial_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index b6d6dcfc4b6..f5ac61b6975 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -68,6 +68,7 @@ static const struct object_ops handler_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index c2dfa8fb8ce..080efc98942 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -173,6 +173,7 @@ static const struct object_ops sock_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1175,6 +1176,7 @@ static const struct object_ops ifchange_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1396,6 +1398,7 @@ static const struct object_ops socket_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 07f3c924f25..0e3e9ee9864 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 1a245c58396..1fb3603f2a7 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -52,6 +52,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + + #ifdef __i386__ +@@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -150,6 +152,7 @@ static const struct object_ops context_ops = + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -173,6 +176,7 @@ static void dump_thread( struct object *obj, int verbose ); + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -187,6 +191,7 @@ static const struct object_ops thread_ops = + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ + thread_get_esync_fd, /* get_esync_fd */ ++ thread_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -228,6 +233,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->entry_point = 0; + thread->esync_fd = -1; + thread->esync_apc_fd = -1; ++ thread->fsync_idx = 0; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_fsync()) ++ { ++ thread->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); ++ } ++ + if (do_esync()) + { + thread->esync_fd = esync_create_fd( 0, 0 ); +@@ -484,6 +496,13 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) + return thread->esync_fd; + } + ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return thread->fsync_idx; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -533,6 +552,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca + apc->result.type = APC_NONE; + if (owner) grab_object( owner ); + } ++ + return apc; + } + +@@ -1068,6 +1088,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_fsync()) ++ fsync_wake_up( obj ); ++ + if (do_esync()) + esync_wake_up( obj ); + +@@ -1158,6 +1181,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + { + wake_thread( thread ); + ++ if (do_fsync() && queue == &thread->user_apc) ++ fsync_wake_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); + } +@@ -1208,6 +1234,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + list_remove( ptr ); + } + ++ if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ fsync_clear_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) + esync_clear( thread->esync_apc_fd ); + +@@ -1327,6 +1356,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_fsync()) ++ fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); + if (violent_death) +diff --git a/server/thread.h b/server/thread.h +index 0f6108b684a..53627631343 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -56,6 +56,8 @@ struct thread + struct list mutex_list; /* list of currently owned mutexes */ + int esync_fd; /* esync file descriptor (signalled on exit) */ + int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ ++ unsigned int fsync_idx; ++ unsigned int fsync_apc_idx; + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index dcbc9e2ece5..9d9d7b8b40c 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -37,6 +37,7 @@ + #include "handle.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -50,11 +51,13 @@ struct timer + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -69,6 +72,7 @@ static const struct object_ops timer_ops = + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ + timer_get_esync_fd, /* get_esync_fd */ ++ timer_get_fsync_idx, /* get_fsync_idx */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->thread = NULL; + timer->esync_fd = -1; + ++ if (do_fsync()) ++ timer->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + timer->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &timer->obj ); ++ + if (do_esync()) + esync_clear( timer->esync_fd ); + } +@@ -223,6 +233,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) + return timer->esync_fd; + } + ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; ++ return timer->fsync_idx; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 0f128728b0f..5a80b6fd50e 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -148,6 +148,7 @@ static const struct object_ops token_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 1a031248a7c..388b116bc67 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -109,6 +109,7 @@ static const struct object_ops window_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From f072cf6366aa8385efdcb4a7b4c49e122f00b750 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Mon, 14 Feb 2022 12:51:27 -0500 +Subject: [PATCH] fsync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/fsync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 39d969f061d..d468782667a 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -576,6 +576,9 @@ NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != FSYNC_MANUAL_EVENT && obj->type != FSYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-d5f2344.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-d5f2344.patch new file mode 100644 index 000000000..8906c4fcb --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-mainline-d5f2344.patch @@ -0,0 +1,3715 @@ +From 1d3dadfd7707ac176b67b50a0dc573f799778836 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:53:31 +0200 +Subject: Fsync rebased 5.13+ mainline + + +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index c96a62ae006..a9be561d87a 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -48,6 +48,7 @@ C_SRCS = \ + unix/env.c \ + unix/esync.c \ + unix/file.c \ ++ unix/fsync.c \ + unix/loader.c \ + unix/process.c \ + unix/registry.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index ed801c71991..ac0326b1aba 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -57,6 +57,7 @@ + + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(esync); + +@@ -66,7 +67,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + return ret; + } + +- if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) + msgwait = TRUE; + + if (has_esync && has_server) +@@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + } + } + +- if (wait_any || count == 1) ++ if (wait_any || count <= 1) + { + /* Try to check objects now, so we can obviate poll() at least. */ + for (i = 0; i < count; i++) +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +new file mode 100644 +index 00000000000..5012425b95a +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.c +@@ -0,0 +1,1267 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#define NONAMELESSUNION ++#include "windef.h" ++#include "winternl.h" ++#include "wine/debug.h" ++#include "wine/server.h" ++ ++#include "unix_private.h" ++#include "fsync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(fsync); ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++ int bitset; ++}; ++#include "poppack.h" ++ ++static inline void small_pause(void) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++} ++ ++static unsigned int spincount = 100; ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ if (getenv("WINEFSYNC_SPINCOUNT")) ++ spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); ++ } ++ ++ return do_fsync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("futexes not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct fsync ++{ ++ enum fsync_type type; ++ void *shm; /* pointer to shm section */ ++}; ++ ++struct semaphore ++{ ++ int count; ++ int max; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct event ++{ ++ int signaled; ++ int unused; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) ++#define FSYNC_LIST_ENTRIES 256 ++ ++static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; ++static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / FSYNC_LIST_BLOCK_SIZE; ++ return idx % FSYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fsync_list[0] = fsync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ fsync_list[entry] = ptr; ++ } ++ } ++ ++ if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) ++ fsync_list[entry][idx].shm = shm; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++static struct fsync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; ++ if (!fsync_list[entry][idx].type) return NULL; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper fsync object (i.e. an event, ++ * semaphore, etc. created using create_fsync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ unsigned int shm_idx = 0; ++ enum fsync_type type; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ SERVER_START_REQ( get_fsync_idx ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); ++ ++ *obj = add_to_list( handle, type, get_shm( shm_idx ) ); ++ return ret; ++} ++ ++NTSTATUS fsync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) ++ { ++ if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) ++ return STATUS_SUCCESS; ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ unsigned int shm_idx; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ SERVER_START_REQ( create_fsync ) ++ { ++ req->access = access; ++ req->low = low; ++ req->high = high; ++ req->type = type; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx )); ++ TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ unsigned int shm_idx; ++ ++ SERVER_START_REQ( open_fsync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx ) ); ++ ++ TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); ++ } ++ return ret; ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_fsync()) ++ { ++ /* make sure the server isn't running with WINEFSYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEFSYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} ++ ++NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); ++ ++ if (prev) *prev = current; ++ ++ futex_wake( &semaphore->count, INT_MAX ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); ++} ++ ++NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); ++} ++ ++NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ out->EventState = event->signaled; ++ out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_fsync( FSYNC_MUTEX, handle, access, attr, ++ initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); ++} ++ ++NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) ++{ ++ struct mutex *mutex; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ if (!--mutex->count) ++ { ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) ++{ ++ int ret; ++ ++ if (alertable) ++ { ++ int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; ++ struct futex_wait_block futexes[2]; ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ ++ futexes[0].addr = addr; ++ futexes[0].val = val; ++ futexes[1].addr = apc_futex; ++ futexes[1].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[0].pad = futexes[1].pad = 0; ++#endif ++ futexes[0].bitset = futexes[1].bitset = ~0; ++ ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait_multiple( futexes, 2, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, 2, NULL ); ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ } ++ else ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait( addr, val, &tmo_p ); ++ } ++ else ++ ret = futex_wait( addr, val, NULL ); ++ } ++ ++ if (!ret) ++ return 0; ++ else if (ret < 0 && errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero = {0}; ++ ++ struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ int has_fsync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ int dummy_futex = 0; ++ unsigned int spin; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD waitcount; ++ ULONGLONG end; ++ int i, ret; ++ ++ /* Grab the APC futex if we don't already have it. */ ++ if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) ++ { ++ unsigned int idx = 0; ++ SERVER_START_REQ( get_fsync_apc_idx ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ idx = reply->shm_idx; ++ } ++ SERVER_END_REQ; ++ ++ if (idx) ++ { ++ struct event *apc_event = get_shm( idx ); ++ ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; ++ } ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart > 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_fsync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_fsync && has_server) ++ FIXME("Can't wait on fsync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(fsync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); ++ } ++ } ++ ++ if (wait_any || count <= 1) ++ { ++ while (1) ++ { ++ /* Try to grab anything. */ ++ ++ if (alertable) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) ++ goto userapc; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj) ++ { ++ if (!obj->type) /* gcc complains if we put this in the switch */ ++ { ++ /* Someone probably closed an object while waiting on it. */ ++ WARN("Handle %p has type 0; was it closed?\n", handles[i]); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ int current; ++ ++ /* It would be a little clearer (and less error-prone) ++ * to use a dedicated interlocked_dec_if_nonzero() ++ * helper, but nesting loops like that is probably not ++ * great for performance... */ ++ for (spin = 0; spin <= spincount || current; ++spin) ++ { ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &semaphore->count; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &mutex->tid; ++ futexes[i].val = tid; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MANUAL_EVENT: ++ case FSYNC_MANUAL_SERVER: ++ case FSYNC_QUEUE: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ default: ++ ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); ++ assert(0); ++ } ++ } ++ else ++ { ++ /* Avoid breaking things entirely. */ ++ futexes[i].addr = &dummy_futex; ++ futexes[i].val = dummy_futex; ++ } ++ ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ } ++ ++ if (alertable) ++ { ++ /* We already checked if it was signaled; don't bother doing it again. */ ++ futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; ++ futexes[i].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ i++; ++ } ++ waitcount = i; ++ ++ /* Looks like everything is contended, so wait. */ ++ ++ if (timeout && !timeout->QuadPart) ++ { ++ /* Unlike esync, we already know that we've timed out, so we ++ * can avoid a syscall. */ ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else if (timeout) ++ { ++ LONGLONG timeleft = update_timeout( end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ++ ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ++ /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, ++ * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to ++ * try again, bad address is already handled by the fact that we ++ * tried to read from it, so only break out on a timeout. */ ++ if (ret == -1 && errno == ETIMEDOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ } /* while (1) */ ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ NTSTATUS status = STATUS_SUCCESS; ++ int current; ++ ++ while (1) ++ { ++ BOOL abandoned; ++ ++tryagain: ++ abandoned = FALSE; ++ ++ /* First step: try to wait on each object in sequence. */ ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ ++ while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) ++ { ++ status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ else if (obj) ++ { ++ /* this works for semaphores too */ ++ struct event *event = obj->shm; ++ ++ while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ ++ if (status == STATUS_TIMEOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return status; ++ } ++ else if (status == STATUS_USER_APC) ++ goto userapc; ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ ++ if (tid && tid != ~0 && tid != GetCurrentThreadId()) ++ goto tryagain; ++ } ++ else if (obj) ++ { ++ struct event *event = obj->shm; ++ ++ if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ goto tryagain; ++ } ++ } ++ ++ /* Yep, still signaled. Now quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ if (tid == GetCurrentThreadId()) ++ break; ++ if (tid && tid != ~0) ++ goto tooslow; ++ if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) ++ goto tooslow; ++ if (tid == ~0) ++ abandoned = TRUE; ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ goto tooslow; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ goto tooslow; ++ break; ++ } ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. ++ * Make sure to let ourselves know that we grabbed the mutexes. */ ++ for (i = 0; i < count; i++) ++ { ++ if (objs[i] && objs[i]->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = objs[i]->shm; ++ mutex->count++; ++ } ++ } ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ ++tooslow: ++ for (--i; i >= 0; i--) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ /* HACK: This won't do the right thing with abandoned ++ * mutexes, but fixing it is probably more trouble than ++ * it's worth. */ ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ __sync_fetch_and_add( &semaphore->count, 1 ); ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ default: ++ /* doesn't need to be put back */ ++ break; ++ } ++ } ++ } /* while (1) */ ++ } /* else (wait-all) */ ++ ++ assert(0); /* shouldn't reach here... */ ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_wait(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* Like esync, we need to let the server know when we are doing a message wait, ++ * and when we are done with one, so that all of the code surrounding hung ++ * queues works, and we also need this for WaitForInputIdle(). ++ * ++ * Unlike esync, we can't wait on the queue fd itself locally. Instead we let ++ * the server do that for us, the way it normally does. This could actually ++ * work for esync too, and that might be better. */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( fsync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from fsync_wait_objects(). */ ++NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ ret = fsync_release_semaphore( signal, 1, NULL ); ++ break; ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_MANUAL_EVENT: ++ ret = fsync_set_event( signal, NULL ); ++ break; ++ case FSYNC_MUTEX: ++ ret = fsync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} +diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h +new file mode 100644 +index 00000000000..b3604548554 +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.h +@@ -0,0 +1,49 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void) DECLSPEC_HIDDEN; ++extern void fsync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 19b73256ef2..d0d979248f8 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -87,6 +87,7 @@ + #include "winternl.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1565,6 +1566,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ fsync_init(); + esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 34d4c80eee5..c48a6339fee 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -95,6 +95,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -1698,6 +1699,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + NTSTATUS ret; + int fd = remove_fd_from_cache( handle ); + ++ if (do_fsync()) ++ fsync_close( handle ); ++ + if (do_esync()) + esync_close( handle ); + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 051c672bd12..d8a8eb526a1 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -73,6 +73,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -323,6 +324,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_fsync()) ++ return fsync_create_semaphore( handle, access, attr, initial, max ); ++ + if (do_esync()) + return esync_create_semaphore( handle, access, attr, initial, max ); + +@@ -349,6 +353,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_open_semaphore( handle, access, attr ); ++ + if (do_esync()) + return esync_open_semaphore( handle, access, attr ); + +@@ -388,6 +395,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_semaphore( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + +@@ -413,6 +423,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_semaphore( handle, count, previous ); ++ + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + +@@ -440,6 +453,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + ++ if (do_fsync()) ++ return fsync_create_event( handle, access, attr, type, state ); ++ + if (do_esync()) + return esync_create_event( handle, access, attr, type, state ); + +@@ -470,6 +486,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_event( handle, access, attr ); ++ + if (do_esync()) + return esync_open_event( handle, access, attr ); + +@@ -495,6 +514,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_set_event( handle, prev_state ); ++ + if (do_esync()) + return esync_set_event( handle ); + +@@ -517,6 +539,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_reset_event( handle, prev_state ); ++ + if (do_esync()) + return esync_reset_event( handle ); + +@@ -549,6 +574,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_pulse_event( handle, prev_state ); ++ + if (do_esync()) + return esync_pulse_event( handle ); + +@@ -584,6 +612,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_event( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + +@@ -612,6 +643,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_create_mutex( handle, access, attr, owned ); ++ + if (do_esync()) + return esync_create_mutex( handle, access, attr, owned ); + +@@ -641,6 +675,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_mutex( handle, access, attr ); ++ + if (do_esync()) + return esync_open_mutex( handle, access, attr ); + +@@ -666,6 +703,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_mutex( handle, prev_count ); ++ + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + +@@ -699,6 +739,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_mutex( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + +@@ -1309,6 +1352,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (do_esync()) + { + NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1341,6 +1391,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_fsync()) ++ return fsync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (do_esync()) + return esync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1374,7 +1427,24 @@ NTSTATUS WINAPI NtYieldExecution(void) + NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) + { + /* if alertable, we need to query the server */ +- if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ if (alertable) ++ { ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ } + + if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index e7033932bcb..a2f485d892f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -59,6 +59,7 @@ struct ntdll_thread_data + struct debug_info *debug_info; /* info for debugstr functions */ + void *start_stack; /* stack for thread startup */ + int esync_apc_fd; /* fd to wait on for user APCs */ ++ int *fsync_apc_futex; + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index bd89649e769..608c83b579c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2683,6 +2683,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data->esync_apc_fd = -1; ++ thread_data->fsync_apc_futex = NULL; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/server/Makefile.in b/server/Makefile.in +index 8bd612b4728..6dc5c465e52 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -15,6 +15,7 @@ C_SRCS = \ + event.c \ + fd.c \ + file.c \ ++ fsync.c \ + handle.c \ + hook.c \ + mach.c \ +diff --git a/server/async.c b/server/async.c +index 1b4f86a1b8b..ceff9c75c27 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -71,6 +71,7 @@ static const struct object_ops async_ops = + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index 73d858fef82..7acef97c30d 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 85afb0cbdc5..7eea72757c5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -116,6 +116,7 @@ static const struct object_ops dir_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index 54a5fb683cc..1fa61b67a88 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index ef66260c991..2149d0bbf32 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -65,6 +65,7 @@ static const struct object_ops completion_ops = + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index 78635f3fae1..824b930bba0 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -43,6 +43,7 @@ + #include "winternl.h" + #include "wine/condrv.h" + #include "esync.h" ++#include "fsync.h" + + struct screen_buffer; + +@@ -82,6 +83,7 @@ static const struct object_ops console_input_ops = + remove_queue, /* remove_queue */ + console_input_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -136,6 +138,7 @@ struct console_server + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -146,6 +149,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + static struct object *console_server_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); + static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); + + static const struct object_ops console_server_ops = + { +@@ -156,6 +160,7 @@ static const struct object_ops console_server_ops = + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ + console_server_get_esync_fd, /* get_esync_fd */ ++ console_server_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -224,6 +229,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -272,6 +278,7 @@ static const struct object_ops console_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops input_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + input_device_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops output_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + output_device_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -587,8 +587,13 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ ++ if (do_fsync()) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync()) + esync_clear( server->esync_fd ); ++ + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -774,6 +787,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ + return server->esync_fd; + } + ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return server->fsync_idx; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -806,6 +828,9 @@ static struct object *create_console_server( void ) + } + allow_fd_caching(server->fd); + ++ if (do_fsync()) ++ server->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + server->esync_fd = esync_create_fd( 0, 0 ); + +@@ -1545,6 +1560,10 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + } +@@ -1645,6 +1664,10 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + +diff --git a/server/debugger.c b/server/debugger.c +index c37f97aa0b6..b7d88cfd2f2 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -103,6 +104,7 @@ static const struct object_ops debug_ctx_ops = + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index b8ce131c732..c56151c9b18 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -40,6 +40,7 @@ + #include "request.h" + #include "process.h" + #include "esync.h" ++#include "fsync.h" + + /* IRP object */ + +@@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,11 +99,13 @@ struct device_manager + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ + device_manager_get_esync_fd, /* get_esync_fd */ ++ device_manager_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -158,6 +163,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ fsync_clear( &file->device->manager->obj ); ++ + if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) + esync_clear( file->device->manager->esync_fd ); + +@@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ + return manager->esync_fd; + } + ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return manager->fsync_idx; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); + ++ if (do_fsync()) ++ manager->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + manager->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) + if (irp->file) grab_object( irp ); + manager->current_call = irp; + ++ if (do_fsync() && list_empty( &manager->requests )) ++ fsync_clear( &manager->obj ); ++ + if (do_esync() && list_empty( &manager->requests )) + esync_clear( manager->esync_fd ); + } +diff --git a/server/directory.c b/server/directory.c +index 007ec1002ac..62f2e50916f 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -59,6 +59,7 @@ static const struct object_ops object_type_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +100,7 @@ static const struct object_ops directory_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +index 5c641fdbe01..6395ba4378c 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "file.h" + #include "esync.h" ++#include "fsync.h" + + int do_esync(void) + { +@@ -51,7 +52,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -130,6 +131,7 @@ const struct object_ops esync_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/event.c b/server/event.c +index 8607b494b6d..28a52e61d00 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -36,6 +36,7 @@ + #include "request.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -44,6 +45,7 @@ struct event + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -51,6 +53,7 @@ static struct object_type *event_get_type( struct object *obj ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); + static void event_destroy( struct object *obj ); +@@ -65,6 +68,7 @@ static const struct object_ops event_ops = + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ + event_get_esync_fd, /* get_esync_fd */ ++ event_get_fsync_idx, /* get_fsync_idx */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + event->manual_reset = manual_reset; + event->signaled = initial_state; + ++ if (do_fsync()) ++ event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); ++ + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); + } +@@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { + struct object *obj; ++ ++ if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) + return (struct event *)obj; /* even though it's not an event */ + +@@ -155,10 +167,19 @@ void pulse_event( struct event *event ) + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); + event->signaled = 0; ++ ++ if (do_fsync()) ++ fsync_clear( &event->obj ); + } + + void set_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_set_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_set_event( (struct esync *)event ); +@@ -172,6 +193,12 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_reset_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_reset_event( (struct esync *)event ); +@@ -179,6 +206,9 @@ void reset_event( struct event *event ) + } + event->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &event->obj ); ++ + if (do_esync()) + esync_clear( event->esync_fd ); + } +@@ -211,6 +241,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) + return event->esync_fd; + } + ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return event->fsync_idx; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +diff --git a/server/fd.c b/server/fd.c +index bbc2462163d..c558229c3c8 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -103,6 +103,7 @@ + #include "process.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -205,6 +206,7 @@ struct fd + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -219,6 +221,7 @@ static const struct object_ops fd_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -261,6 +264,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -302,6 +306,7 @@ static const struct object_ops inode_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) + fd->completion = NULL; + fd->comp_flags = 0; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) + if (do_esync()) + fd->esync_fd = esync_create_fd( 1, 0 ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + fd->esync_fd = esync_create_fd( 0, 0 ); ++ + return fd; + } + +@@ -2190,6 +2205,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); + ++ if (do_fsync() && !signaled) ++ fsync_clear( fd->user ); ++ + if (do_esync() && !signaled) + esync_clear( fd->esync_fd ); + } +@@ -2232,6 +2250,15 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) + return ret; + } + ++unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ unsigned int ret = fd->fsync_idx; ++ *type = FSYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 49c4ba32b42..829c8685d63 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -112,6 +112,7 @@ static const struct object_ops file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index cae0ac1e395..ed030af8d8f 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -103,6 +103,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); ++extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/fsync.c b/server/fsync.c +new file mode 100644 +index 00000000000..0a88ecdd3f6 +--- /dev/null ++++ b/server/fsync.c +@@ -0,0 +1,524 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "fsync.h" ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++}; ++#include "poppack.h" ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ } ++ ++ return do_fsync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static int is_fsync_initialized; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if (!shm_unlink( shm_name )) ++ fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ is_fsync_initialized = 1; ++ ++ fprintf( stderr, "fsync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct fsync ++{ ++ struct object obj; ++ unsigned int shm_idx; ++ enum fsync_type type; ++ struct list mutex_entry; ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ); ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ); ++static void fsync_destroy( struct object *obj ); ++ ++const struct object_ops fsync_ops = ++{ ++ sizeof(struct fsync), /* size */ ++ &no_type, /* type */ ++ fsync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* get_esync_fd */ ++ fsync_get_fsync_idx, /* get_fsync_idx */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ fsync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ fsync_destroy /* destroy */ ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ assert( obj->ops == &fsync_ops ); ++ fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); ++} ++ ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ *type = fsync->type; ++ return fsync->shm_idx; ++} ++ ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void fsync_destroy( struct object *obj ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ if (fsync->type == FSYNC_MUTEX) ++ list_remove( &fsync->mutex_entry ); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++/* FIXME: This is rather inefficient... */ ++static unsigned int shm_idx_counter = 1; ++ ++unsigned int fsync_alloc_shm( int low, int high ) ++{ ++#ifdef __linux__ ++ int shm_idx; ++ int *shm; ++ ++ /* this is arguably a bit of a hack, but we need some way to prevent ++ * allocating shm for the master socket */ ++ if (!is_fsync_initialized) ++ return 0; ++ ++ shm_idx = shm_idx_counter++; ++ ++ while (shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ shm = get_shm( shm_idx ); ++ assert(shm); ++ shm[0] = low; ++ shm[1] = high; ++ ++ return shm_idx; ++#else ++ return 0; ++#endif ++} ++ ++static int type_matches( enum fsync_type type1, enum fsync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && ++ (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); ++} ++ ++struct fsync *create_fsync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int low, int high, enum fsync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef __linux__ ++ struct fsync *fsync; ++ ++ if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ /* initialize it if it didn't already exist */ ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ ++ fsync->shm_idx = fsync_alloc_shm( low, high ); ++ fsync->type = type; ++ if (type == FSYNC_MUTEX) ++ list_add_tail( &mutex_list, &fsync->mutex_entry ); ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, fsync->type )) ++ { ++ release_object( &fsync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ ++ return fsync; ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++/* shm layout for events or event-like objects. */ ++struct fsync_event ++{ ++ int signaled; ++ int unused; ++}; ++ ++void fsync_wake_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_wake_up( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_up: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_clear_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++void fsync_clear( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_set_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_reset_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++ ++void fsync_abandon_mutexes( struct thread *thread ) ++{ ++ struct fsync *fsync; ++ ++ LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( fsync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_fsync) ++{ ++ struct fsync *fsync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_fsync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, ++ req->high, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, fsync, ++ req->access, objattr->attributes ); ++ ++ reply->shm_idx = fsync->shm_idx; ++ reply->type = fsync->type; ++ release_object( fsync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_fsync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &fsync_ops, &name, req->attributes ); ++ ++ if (reply->handle) ++ { ++ struct fsync *fsync; ++ ++ if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, ++ 0, &fsync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, fsync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( fsync ); ++ return; ++ } ++ ++ reply->type = fsync->type; ++ reply->shm_idx = fsync->shm_idx; ++ release_object( fsync ); ++ } ++} ++ ++/* Retrieve the index of a shm section which will be signaled by the server. */ ++DECL_HANDLER(get_fsync_idx) ++{ ++ struct object *obj; ++ enum fsync_type type; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_fsync_idx) ++ { ++ reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); ++ reply->type = type; ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++DECL_HANDLER(get_fsync_apc_idx) ++{ ++ reply->shm_idx = current->fsync_apc_idx; ++} +diff --git a/server/fsync.h b/server/fsync.h +new file mode 100644 +index 00000000000..a91939b7f0a +--- /dev/null ++++ b/server/fsync.h +@@ -0,0 +1,34 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern unsigned int fsync_alloc_shm( int low, int high ); ++extern void fsync_wake_futex( unsigned int shm_idx ); ++extern void fsync_clear_futex( unsigned int shm_idx ); ++extern void fsync_wake_up( struct object *obj ); ++extern void fsync_clear( struct object *obj ); ++ ++struct fsync; ++ ++extern const struct object_ops fsync_ops; ++extern void fsync_set_event( struct fsync *fsync ); ++extern void fsync_reset_event( struct fsync *fsync ); ++extern void fsync_abandon_mutexes( struct thread *thread ); +diff --git a/server/handle.c b/server/handle.c +index cb5628b7e06..ff44446acc9 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 61b5014c442..379f9c074d5 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index 18fef4b0466..b541524c9a4 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 3e02cbb3832..7a09436b637 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -37,6 +37,7 @@ + #include "thread.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + /* command-line options */ + int debug_level = 0; +@@ -141,8 +142,14 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_fsync()) ++ fsync_init(); ++ + if (do_esync()) + esync_init(); ++ ++ if (!do_fsync() && !do_esync()) ++ fprintf( stderr, "wineserver: using server-side synchronization.\n" ); + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); +diff --git a/server/mapping.c b/server/mapping.c +index 10def3ca694..d55df5145f7 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index 1235ab4731f..ccdc383f0df 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index e59a5b6c183..d0ea32a24d5 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 5b6bb9cbfe1..4c92b174ef3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -70,6 +70,8 @@ struct object_ops + int (*signaled)(struct object *,struct wait_queue_entry *); + /* return the esync fd for this object */ + int (*get_esync_fd)(struct object *, enum esync_type *type); ++ /* return the fsync shm idx for this object */ ++ unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index df50955f621..c4f51dcad30 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -50,6 +50,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + /* process object */ + +@@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -81,6 +83,7 @@ static const struct object_ops process_ops = + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ + process_get_esync_fd, /* get_esync_fd */ ++ process_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -180,6 +184,7 @@ static const struct object_ops job_ops = + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -547,6 +552,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + process->esync_fd = -1; ++ process->fsync_idx = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -603,6 +609,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_fsync()) ++ process->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + process->esync_fd = esync_create_fd( 0, 0 ); + +@@ -685,6 +694,13 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) + return process->esync_fd; + } + ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return process->fsync_idx; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index eec69ddbcaf..abb4ed31b48 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -99,6 +99,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ ++ unsigned int fsync_idx; + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 2a8662354f6..381bd8d8218 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3766,3 +3766,57 @@ enum esync_type + /* Retrieve the fd to wait on for user APCs. */ + @REQ(get_esync_apc_fd) + @END ++ ++enum fsync_type ++{ ++ FSYNC_SEMAPHORE = 1, ++ FSYNC_AUTO_EVENT, ++ FSYNC_MANUAL_EVENT, ++ FSYNC_MUTEX, ++ FSYNC_AUTO_SERVER, ++ FSYNC_MANUAL_SERVER, ++ FSYNC_QUEUE, ++}; ++ ++/* Create a new futex-based synchronization object */ ++@REQ(create_fsync) ++ unsigned int access; /* wanted access rights */ ++ int low; /* initial value of low word */ ++ int high; /* initial value of high word */ ++ int type; /* type of fsync object */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Open an fsync object */ ++@REQ(open_fsync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of fsync object */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the shm index for an object. */ ++@REQ(get_fsync_idx) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++@REQ(fsync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++@REQ(get_fsync_apc_idx) ++@REPLY ++ unsigned int shm_idx; ++@END +diff --git a/server/queue.c b/server/queue.c +index a78748b96ca..dccf43197cf 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "user.h" + #include "esync.h" ++#include "fsync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -147,6 +148,8 @@ struct msg_queue + unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ + int esync_fd; /* esync file descriptor (signalled on message) */ + int esync_in_msgwait; /* our thread is currently waiting on us */ ++ unsigned int fsync_idx; ++ int fsync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -164,6 +167,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -180,6 +184,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ + msg_queue_get_esync_fd, /* get_esync_fd */ ++ msg_queue_get_fsync_idx, /* get_fsync_idx */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops thread_input_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -321,12 +327,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->ignore_post_msg = 0; + queue->esync_fd = -1; + queue->esync_in_msgwait = 0; ++ queue->fsync_idx = 0; ++ queue->fsync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); + ++ if (do_fsync()) ++ queue->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + queue->esync_fd = esync_create_fd( 0, 0 ); + +@@ -509,6 +520,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + queue->wake_bits &= ~bits; + queue->changed_bits &= ~bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -970,6 +984,9 @@ static int is_queue_hung( struct msg_queue *queue ) + return 0; /* thread is waiting on queue -> not hung */ + } + ++ if (do_fsync() && queue->fsync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + +@@ -1035,6 +1052,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) + return queue->esync_fd; + } + ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = FSYNC_QUEUE; ++ return queue->fsync_idx; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2517,6 +2541,9 @@ DECL_HANDLER(get_queue_status) + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -3536,3 +3563,18 @@ DECL_HANDLER(esync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fsync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->fsync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -168,6 +168,7 @@ static const struct object_ops key_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 20b0ec309f3..9fd08139375 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index d7d3a24e48f..604721d0d5f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index a50ace9903f..78d33460892 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -93,6 +93,7 @@ static const struct object_ops serial_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index b6d6dcfc4b6..f5ac61b6975 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -68,6 +68,7 @@ static const struct object_ops handler_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index c2dfa8fb8ce..080efc98942 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -173,6 +173,7 @@ static const struct object_ops sock_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1175,6 +1176,7 @@ static const struct object_ops ifchange_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1396,6 +1398,7 @@ static const struct object_ops socket_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 07f3c924f25..0e3e9ee9864 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 1a245c58396..1fb3603f2a7 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -52,6 +52,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + + #ifdef __i386__ +@@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -150,6 +152,7 @@ static const struct object_ops context_ops = + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -173,6 +176,7 @@ static void dump_thread( struct object *obj, int verbose ); + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -187,6 +191,7 @@ static const struct object_ops thread_ops = + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ + thread_get_esync_fd, /* get_esync_fd */ ++ thread_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -228,6 +233,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->entry_point = 0; + thread->esync_fd = -1; + thread->esync_apc_fd = -1; ++ thread->fsync_idx = 0; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_fsync()) ++ { ++ thread->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); ++ } ++ + if (do_esync()) + { + thread->esync_fd = esync_create_fd( 0, 0 ); +@@ -484,6 +496,13 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) + return thread->esync_fd; + } + ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return thread->fsync_idx; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -533,6 +552,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca + apc->result.type = APC_NONE; + if (owner) grab_object( owner ); + } ++ + return apc; + } + +@@ -1068,6 +1088,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_fsync()) ++ fsync_wake_up( obj ); ++ + if (do_esync()) + esync_wake_up( obj ); + +@@ -1158,6 +1181,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + { + wake_thread( thread ); + ++ if (do_fsync() && queue == &thread->user_apc) ++ fsync_wake_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); + } +@@ -1208,6 +1234,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + list_remove( ptr ); + } + ++ if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ fsync_clear_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) + esync_clear( thread->esync_apc_fd ); + +@@ -1327,6 +1356,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_fsync()) ++ fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); + if (violent_death) +diff --git a/server/thread.h b/server/thread.h +index 0f6108b684a..53627631343 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -56,6 +56,8 @@ struct thread + struct list mutex_list; /* list of currently owned mutexes */ + int esync_fd; /* esync file descriptor (signalled on exit) */ + int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ ++ unsigned int fsync_idx; ++ unsigned int fsync_apc_idx; + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index dcbc9e2ece5..9d9d7b8b40c 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -37,6 +37,7 @@ + #include "handle.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -50,11 +51,13 @@ struct timer + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -69,6 +72,7 @@ static const struct object_ops timer_ops = + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ + timer_get_esync_fd, /* get_esync_fd */ ++ timer_get_fsync_idx, /* get_fsync_idx */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->thread = NULL; + timer->esync_fd = -1; + ++ if (do_fsync()) ++ timer->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + timer->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &timer->obj ); ++ + if (do_esync()) + esync_clear( timer->esync_fd ); + } +@@ -223,6 +233,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) + return timer->esync_fd; + } + ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; ++ return timer->fsync_idx; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 0f128728b0f..5a80b6fd50e 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -148,6 +148,7 @@ static const struct object_ops token_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 1a031248a7c..388b116bc67 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -109,6 +109,7 @@ static const struct object_ops window_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From f072cf6366aa8385efdcb4a7b4c49e122f00b750 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Mon, 14 Feb 2022 12:51:27 -0500 +Subject: [PATCH] fsync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/fsync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 39d969f061d..d468782667a 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -576,6 +576,9 @@ NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != FSYNC_MANUAL_EVENT && obj->type != FSYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-c14de4c.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-c14de4c.patch new file mode 100644 index 000000000..c287e5832 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-c14de4c.patch @@ -0,0 +1,3762 @@ +From 1d3dadfd7707ac176b67b50a0dc573f799778836 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:53:31 +0200 +Subject: Fsync rebased 5.13+ staging + + +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index c96a62ae006..a9be561d87a 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -48,6 +48,7 @@ C_SRCS = \ + unix/env.c \ + unix/esync.c \ + unix/file.c \ ++ unix/fsync.c \ + unix/loader.c \ + unix/process.c \ + unix/registry.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index ed801c71991..ac0326b1aba 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -57,6 +57,7 @@ + + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(esync); + +@@ -66,7 +67,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + return ret; + } + +- if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) + msgwait = TRUE; + + if (has_esync && has_server) +@@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + } + } + +- if (wait_any || count == 1) ++ if (wait_any || count <= 1) + { + /* Try to check objects now, so we can obviate poll() at least. */ + for (i = 0; i < count; i++) +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +new file mode 100644 +index 00000000000..5012425b95a +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.c +@@ -0,0 +1,1266 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++#include "wine/debug.h" ++#include "wine/server.h" ++ ++#include "unix_private.h" ++#include "fsync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(fsync); ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++ int bitset; ++}; ++#include "poppack.h" ++ ++static inline void small_pause(void) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++} ++ ++static unsigned int spincount = 100; ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ if (getenv("WINEFSYNC_SPINCOUNT")) ++ spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); ++ } ++ ++ return do_fsync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("futexes not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct fsync ++{ ++ enum fsync_type type; ++ void *shm; /* pointer to shm section */ ++}; ++ ++struct semaphore ++{ ++ int count; ++ int max; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct event ++{ ++ int signaled; ++ int unused; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) ++#define FSYNC_LIST_ENTRIES 256 ++ ++static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; ++static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / FSYNC_LIST_BLOCK_SIZE; ++ return idx % FSYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fsync_list[0] = fsync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ fsync_list[entry] = ptr; ++ } ++ } ++ ++ if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) ++ fsync_list[entry][idx].shm = shm; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++static struct fsync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; ++ if (!fsync_list[entry][idx].type) return NULL; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper fsync object (i.e. an event, ++ * semaphore, etc. created using create_fsync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ unsigned int shm_idx = 0; ++ enum fsync_type type; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ SERVER_START_REQ( get_fsync_idx ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); ++ ++ *obj = add_to_list( handle, type, get_shm( shm_idx ) ); ++ return ret; ++} ++ ++NTSTATUS fsync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) ++ { ++ if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) ++ return STATUS_SUCCESS; ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ unsigned int shm_idx; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ SERVER_START_REQ( create_fsync ) ++ { ++ req->access = access; ++ req->low = low; ++ req->high = high; ++ req->type = type; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx )); ++ TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ unsigned int shm_idx; ++ ++ SERVER_START_REQ( open_fsync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx ) ); ++ ++ TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); ++ } ++ return ret; ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_fsync()) ++ { ++ /* make sure the server isn't running with WINEFSYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEFSYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} ++ ++NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); ++ ++ if (prev) *prev = current; ++ ++ futex_wake( &semaphore->count, INT_MAX ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); ++} ++ ++NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); ++} ++ ++NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ out->EventState = event->signaled; ++ out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_fsync( FSYNC_MUTEX, handle, access, attr, ++ initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); ++} ++ ++NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) ++{ ++ struct mutex *mutex; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ if (!--mutex->count) ++ { ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) ++{ ++ int ret; ++ ++ if (alertable) ++ { ++ int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; ++ struct futex_wait_block futexes[2]; ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ ++ futexes[0].addr = addr; ++ futexes[0].val = val; ++ futexes[1].addr = apc_futex; ++ futexes[1].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[0].pad = futexes[1].pad = 0; ++#endif ++ futexes[0].bitset = futexes[1].bitset = ~0; ++ ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait_multiple( futexes, 2, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, 2, NULL ); ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ } ++ else ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait( addr, val, &tmo_p ); ++ } ++ else ++ ret = futex_wait( addr, val, NULL ); ++ } ++ ++ if (!ret) ++ return 0; ++ else if (ret < 0 && errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero = {0}; ++ ++ struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ int has_fsync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ int dummy_futex = 0; ++ unsigned int spin; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD waitcount; ++ ULONGLONG end; ++ int i, ret; ++ ++ /* Grab the APC futex if we don't already have it. */ ++ if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) ++ { ++ unsigned int idx = 0; ++ SERVER_START_REQ( get_fsync_apc_idx ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ idx = reply->shm_idx; ++ } ++ SERVER_END_REQ; ++ ++ if (idx) ++ { ++ struct event *apc_event = get_shm( idx ); ++ ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; ++ } ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart > 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_fsync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_fsync && has_server) ++ FIXME("Can't wait on fsync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(fsync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); ++ } ++ } ++ ++ if (wait_any || count <= 1) ++ { ++ while (1) ++ { ++ /* Try to grab anything. */ ++ ++ if (alertable) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) ++ goto userapc; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj) ++ { ++ if (!obj->type) /* gcc complains if we put this in the switch */ ++ { ++ /* Someone probably closed an object while waiting on it. */ ++ WARN("Handle %p has type 0; was it closed?\n", handles[i]); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ int current; ++ ++ /* It would be a little clearer (and less error-prone) ++ * to use a dedicated interlocked_dec_if_nonzero() ++ * helper, but nesting loops like that is probably not ++ * great for performance... */ ++ for (spin = 0; spin <= spincount || current; ++spin) ++ { ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &semaphore->count; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &mutex->tid; ++ futexes[i].val = tid; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MANUAL_EVENT: ++ case FSYNC_MANUAL_SERVER: ++ case FSYNC_QUEUE: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ default: ++ ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); ++ assert(0); ++ } ++ } ++ else ++ { ++ /* Avoid breaking things entirely. */ ++ futexes[i].addr = &dummy_futex; ++ futexes[i].val = dummy_futex; ++ } ++ ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ } ++ ++ if (alertable) ++ { ++ /* We already checked if it was signaled; don't bother doing it again. */ ++ futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; ++ futexes[i].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ i++; ++ } ++ waitcount = i; ++ ++ /* Looks like everything is contended, so wait. */ ++ ++ if (timeout && !timeout->QuadPart) ++ { ++ /* Unlike esync, we already know that we've timed out, so we ++ * can avoid a syscall. */ ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else if (timeout) ++ { ++ LONGLONG timeleft = update_timeout( end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ++ ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ++ /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, ++ * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to ++ * try again, bad address is already handled by the fact that we ++ * tried to read from it, so only break out on a timeout. */ ++ if (ret == -1 && errno == ETIMEDOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ } /* while (1) */ ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ NTSTATUS status = STATUS_SUCCESS; ++ int current; ++ ++ while (1) ++ { ++ BOOL abandoned; ++ ++tryagain: ++ abandoned = FALSE; ++ ++ /* First step: try to wait on each object in sequence. */ ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ ++ while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) ++ { ++ status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ else if (obj) ++ { ++ /* this works for semaphores too */ ++ struct event *event = obj->shm; ++ ++ while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ ++ if (status == STATUS_TIMEOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return status; ++ } ++ else if (status == STATUS_USER_APC) ++ goto userapc; ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ ++ if (tid && tid != ~0 && tid != GetCurrentThreadId()) ++ goto tryagain; ++ } ++ else if (obj) ++ { ++ struct event *event = obj->shm; ++ ++ if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ goto tryagain; ++ } ++ } ++ ++ /* Yep, still signaled. Now quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ if (tid == GetCurrentThreadId()) ++ break; ++ if (tid && tid != ~0) ++ goto tooslow; ++ if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) ++ goto tooslow; ++ if (tid == ~0) ++ abandoned = TRUE; ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ goto tooslow; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ goto tooslow; ++ break; ++ } ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. ++ * Make sure to let ourselves know that we grabbed the mutexes. */ ++ for (i = 0; i < count; i++) ++ { ++ if (objs[i] && objs[i]->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = objs[i]->shm; ++ mutex->count++; ++ } ++ } ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ ++tooslow: ++ for (--i; i >= 0; i--) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ /* HACK: This won't do the right thing with abandoned ++ * mutexes, but fixing it is probably more trouble than ++ * it's worth. */ ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ __sync_fetch_and_add( &semaphore->count, 1 ); ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ default: ++ /* doesn't need to be put back */ ++ break; ++ } ++ } ++ } /* while (1) */ ++ } /* else (wait-all) */ ++ ++ assert(0); /* shouldn't reach here... */ ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_wait(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* Like esync, we need to let the server know when we are doing a message wait, ++ * and when we are done with one, so that all of the code surrounding hung ++ * queues works, and we also need this for WaitForInputIdle(). ++ * ++ * Unlike esync, we can't wait on the queue fd itself locally. Instead we let ++ * the server do that for us, the way it normally does. This could actually ++ * work for esync too, and that might be better. */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( fsync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from fsync_wait_objects(). */ ++NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ ret = fsync_release_semaphore( signal, 1, NULL ); ++ break; ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_MANUAL_EVENT: ++ ret = fsync_set_event( signal, NULL ); ++ break; ++ case FSYNC_MUTEX: ++ ret = fsync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} +diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h +new file mode 100644 +index 00000000000..b3604548554 +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.h +@@ -0,0 +1,49 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void) DECLSPEC_HIDDEN; ++extern void fsync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 19b73256ef2..d0d979248f8 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -87,6 +87,7 @@ + #include "winternl.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1565,6 +1566,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ fsync_init(); + esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 34d4c80eee5..c48a6339fee 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -95,6 +95,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -1698,6 +1699,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + NTSTATUS ret; + int fd = remove_fd_from_cache( handle ); + ++ if (do_fsync()) ++ fsync_close( handle ); ++ + if (do_esync()) + esync_close( handle ); + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 051c672bd12..d8a8eb526a1 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -73,6 +73,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -262,6 +262,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_fsync()) ++ return fsync_create_semaphore( handle, access, attr, initial, max ); ++ + if (do_esync()) + return esync_create_semaphore( handle, access, attr, initial, max ); + +@@ -297,6 +297,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_open_semaphore( handle, access, attr ); ++ + if (do_esync()) + return esync_open_semaphore( handle, access, attr ); + +@@ -327,6 +327,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_semaphore( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + +@@ -367,6 +367,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_semaphore( handle, count, previous ); ++ + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + +@@ -394,6 +394,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + ++ if (do_fsync()) ++ return fsync_create_event( handle, access, attr, type, state ); ++ + if (do_esync()) + return esync_create_event( handle, access, attr, type, state ); + +@@ -428,6 +428,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_event( handle, access, attr ); ++ + if (do_esync()) + return esync_open_event( handle, access, attr ); + +@@ -458,6 +458,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_set_event( handle, prev_state ); ++ + if (do_esync()) + return esync_set_event( handle ); + +@@ -480,6 +480,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_reset_event( handle, prev_state ); ++ + if (do_esync()) + return esync_reset_event( handle ); + +@@ -512,6 +512,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_pulse_event( handle, prev_state ); ++ + if (do_esync()) + return esync_pulse_event( handle ); + +@@ -537,6 +537,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_event( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + +@@ -578,6 +578,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_create_mutex( handle, access, attr, owned ); ++ + if (do_esync()) + return esync_create_mutex( handle, access, attr, owned ); + +@@ -611,6 +611,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_mutex( handle, access, attr ); ++ + if (do_esync()) + return esync_open_mutex( handle, access, attr ); + +@@ -641,6 +641,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_mutex( handle, prev_count ); ++ + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + +@@ -665,6 +665,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_mutex( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + +@@ -1286,6 +1286,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (do_esync()) + { + NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1323,6 +1323,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_fsync()) ++ return fsync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (do_esync()) + return esync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1348,7 +1348,24 @@ NTSTATUS WINAPI NtYieldExecution(void) + NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) + { + /* if alertable, we need to query the server */ +- if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ if (alertable) ++ { ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ } + + if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index e7033932bcb..a2f485d892f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -59,6 +59,7 @@ struct ntdll_thread_data + struct debug_info *debug_info; /* info for debugstr functions */ + void *start_stack; /* stack for thread startup */ + int esync_apc_fd; /* fd to wait on for user APCs */ ++ int *fsync_apc_futex; + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index bd89649e769..608c83b579c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2683,6 +2683,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data->esync_apc_fd = -1; ++ thread_data->fsync_apc_futex = NULL; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/server/Makefile.in b/server/Makefile.in +index 8bd612b4728..6dc5c465e52 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -15,6 +15,7 @@ C_SRCS = \ + event.c \ + fd.c \ + file.c \ ++ fsync.c \ + handle.c \ + hook.c \ + mach.c \ +diff --git a/server/async.c b/server/async.c +index 1b4f86a1b8b..ceff9c75c27 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -71,6 +71,7 @@ static const struct object_ops async_ops = + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index 73d858fef82..7acef97c30d 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 85afb0cbdc5..7eea72757c5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -116,6 +116,7 @@ static const struct object_ops dir_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index 54a5fb683cc..1fa61b67a88 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index ef66260c991..2149d0bbf32 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -65,6 +65,7 @@ static const struct object_ops completion_ops = + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index 78635f3fae1..824b930bba0 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -43,6 +43,7 @@ + #include "winternl.h" + #include "wine/condrv.h" + #include "esync.h" ++#include "fsync.h" + + struct screen_buffer; + +@@ -82,6 +83,7 @@ static const struct object_ops console_input_ops = + remove_queue, /* remove_queue */ + console_input_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -136,6 +138,7 @@ struct console_server + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ + int esync_fd; ++ unsigned int fsync_idx; + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -146,6 +149,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + static void console_server_destroy( struct object *obj ); + static int console_server_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static struct fd *console_server_get_fd( struct object *obj ); + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, +@@ -156,6 +160,7 @@ static const struct object_ops console_server_ops = + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ + console_server_get_esync_fd, /* get_esync_fd */ ++ console_server_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -224,6 +229,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -272,6 +278,7 @@ static const struct object_ops console_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops input_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + input_device_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops output_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + output_device_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -587,8 +587,13 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ ++ if (do_fsync()) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync()) + esync_clear( server->esync_fd ); ++ + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -774,6 +787,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ + return server->esync_fd; + } + ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return server->fsync_idx; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -806,6 +828,9 @@ static struct object *create_console_server( void ) + } + allow_fd_caching(server->fd); + ++ if (do_fsync()) ++ server->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + server->esync_fd = esync_create_fd( 0, 0 ); + +@@ -1545,6 +1560,10 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + } +@@ -1645,6 +1664,10 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + +diff --git a/server/debugger.c b/server/debugger.c +index c37f97aa0b6..b7d88cfd2f2 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -103,6 +104,7 @@ static const struct object_ops debug_ctx_ops = + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index b8ce131c732..c56151c9b18 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -40,6 +40,7 @@ + #include "request.h" + #include "process.h" + #include "esync.h" ++#include "fsync.h" + + /* IRP object */ + +@@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,11 +99,13 @@ struct device_manager + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ + device_manager_get_esync_fd, /* get_esync_fd */ ++ device_manager_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -158,6 +163,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ fsync_clear( &file->device->manager->obj ); ++ + if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) + esync_clear( file->device->manager->esync_fd ); + +@@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ + return manager->esync_fd; + } + ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return manager->fsync_idx; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); + ++ if (do_fsync()) ++ manager->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + manager->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) + if (irp->file) grab_object( irp ); + manager->current_call = irp; + ++ if (do_fsync() && list_empty( &manager->requests )) ++ fsync_clear( &manager->obj ); ++ + if (do_esync() && list_empty( &manager->requests )) + esync_clear( manager->esync_fd ); + } +diff --git a/server/directory.c b/server/directory.c +index 007ec1002ac..62f2e50916f 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -59,6 +59,7 @@ static const struct object_ops object_type_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +100,7 @@ static const struct object_ops directory_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +index 5c641fdbe01..6395ba4378c 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "file.h" + #include "esync.h" ++#include "fsync.h" + + int do_esync(void) + { +@@ -51,7 +52,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -130,6 +131,7 @@ const struct object_ops esync_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/event.c b/server/event.c +index 8607b494b6d..28a52e61d00 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -36,6 +36,7 @@ + #include "request.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -44,6 +45,7 @@ struct event + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -51,6 +53,7 @@ static void event_dump( struct object *obj, int verbose ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); + static void event_destroy( struct object *obj ); +@@ -65,6 +68,7 @@ static const struct object_ops event_ops = + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ + event_get_esync_fd, /* get_esync_fd */ ++ event_get_fsync_idx, /* get_fsync_idx */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + event->manual_reset = manual_reset; + event->signaled = initial_state; + ++ if (do_fsync()) ++ event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); ++ + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); + } +@@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { + struct object *obj; ++ ++ if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) + return (struct event *)obj; /* even though it's not an event */ + +@@ -155,10 +167,19 @@ void pulse_event( struct event *event ) + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); + event->signaled = 0; ++ ++ if (do_fsync()) ++ fsync_clear( &event->obj ); + } + + void set_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_set_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_set_event( (struct esync *)event ); +@@ -172,6 +193,12 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_reset_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_reset_event( (struct esync *)event ); +@@ -179,6 +206,9 @@ void reset_event( struct event *event ) + } + event->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &event->obj ); ++ + if (do_esync()) + esync_clear( event->esync_fd ); + } +@@ -211,6 +241,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) + return event->esync_fd; + } + ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return event->fsync_idx; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +diff --git a/server/fd.c b/server/fd.c +index bbc2462163d..c558229c3c8 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -103,6 +103,7 @@ + #include "process.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -205,6 +206,7 @@ struct fd + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -219,6 +221,7 @@ static const struct object_ops fd_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -261,6 +264,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -302,6 +306,7 @@ static const struct object_ops inode_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) + fd->completion = NULL; + fd->comp_flags = 0; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) + if (do_esync()) + fd->esync_fd = esync_create_fd( 1, 0 ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + fd->esync_fd = esync_create_fd( 0, 0 ); ++ + return fd; + } + +@@ -2190,6 +2205,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); + ++ if (do_fsync() && !signaled) ++ fsync_clear( fd->user ); ++ + if (do_esync() && !signaled) + esync_clear( fd->esync_fd ); + } +@@ -2232,6 +2250,15 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) + return ret; + } + ++unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ unsigned int ret = fd->fsync_idx; ++ *type = FSYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 49c4ba32b42..829c8685d63 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -112,6 +112,7 @@ static const struct object_ops file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index cae0ac1e395..ed030af8d8f 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -103,6 +103,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); ++extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/fsync.c b/server/fsync.c +new file mode 100644 +index 00000000000..0a88ecdd3f6 +--- /dev/null ++++ b/server/fsync.c +@@ -0,0 +1,524 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "fsync.h" ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++}; ++#include "poppack.h" ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ } ++ ++ return do_fsync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static int is_fsync_initialized; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if (!shm_unlink( shm_name )) ++ fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ is_fsync_initialized = 1; ++ ++ fprintf( stderr, "fsync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct fsync ++{ ++ struct object obj; ++ unsigned int shm_idx; ++ enum fsync_type type; ++ struct list mutex_entry; ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ); ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ); ++static void fsync_destroy( struct object *obj ); ++ ++const struct object_ops fsync_ops = ++{ ++ sizeof(struct fsync), /* size */ ++ &no_type, /* type */ ++ fsync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* get_esync_fd */ ++ fsync_get_fsync_idx, /* get_fsync_idx */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ fsync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ fsync_destroy /* destroy */ ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ assert( obj->ops == &fsync_ops ); ++ fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); ++} ++ ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ *type = fsync->type; ++ return fsync->shm_idx; ++} ++ ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void fsync_destroy( struct object *obj ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ if (fsync->type == FSYNC_MUTEX) ++ list_remove( &fsync->mutex_entry ); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++/* FIXME: This is rather inefficient... */ ++static unsigned int shm_idx_counter = 1; ++ ++unsigned int fsync_alloc_shm( int low, int high ) ++{ ++#ifdef __linux__ ++ int shm_idx; ++ int *shm; ++ ++ /* this is arguably a bit of a hack, but we need some way to prevent ++ * allocating shm for the master socket */ ++ if (!is_fsync_initialized) ++ return 0; ++ ++ shm_idx = shm_idx_counter++; ++ ++ while (shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ shm = get_shm( shm_idx ); ++ assert(shm); ++ shm[0] = low; ++ shm[1] = high; ++ ++ return shm_idx; ++#else ++ return 0; ++#endif ++} ++ ++static int type_matches( enum fsync_type type1, enum fsync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && ++ (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); ++} ++ ++struct fsync *create_fsync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int low, int high, enum fsync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef __linux__ ++ struct fsync *fsync; ++ ++ if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ /* initialize it if it didn't already exist */ ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ ++ fsync->shm_idx = fsync_alloc_shm( low, high ); ++ fsync->type = type; ++ if (type == FSYNC_MUTEX) ++ list_add_tail( &mutex_list, &fsync->mutex_entry ); ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, fsync->type )) ++ { ++ release_object( &fsync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ ++ return fsync; ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++/* shm layout for events or event-like objects. */ ++struct fsync_event ++{ ++ int signaled; ++ int unused; ++}; ++ ++void fsync_wake_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_wake_up( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_up: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_clear_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++void fsync_clear( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_set_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_reset_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++ ++void fsync_abandon_mutexes( struct thread *thread ) ++{ ++ struct fsync *fsync; ++ ++ LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( fsync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_fsync) ++{ ++ struct fsync *fsync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_fsync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, ++ req->high, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, fsync, ++ req->access, objattr->attributes ); ++ ++ reply->shm_idx = fsync->shm_idx; ++ reply->type = fsync->type; ++ release_object( fsync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_fsync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &fsync_ops, &name, req->attributes ); ++ ++ if (reply->handle) ++ { ++ struct fsync *fsync; ++ ++ if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, ++ 0, &fsync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, fsync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( fsync ); ++ return; ++ } ++ ++ reply->type = fsync->type; ++ reply->shm_idx = fsync->shm_idx; ++ release_object( fsync ); ++ } ++} ++ ++/* Retrieve the index of a shm section which will be signaled by the server. */ ++DECL_HANDLER(get_fsync_idx) ++{ ++ struct object *obj; ++ enum fsync_type type; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_fsync_idx) ++ { ++ reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); ++ reply->type = type; ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++DECL_HANDLER(get_fsync_apc_idx) ++{ ++ reply->shm_idx = current->fsync_apc_idx; ++} +diff --git a/server/fsync.h b/server/fsync.h +new file mode 100644 +index 00000000000..a91939b7f0a +--- /dev/null ++++ b/server/fsync.h +@@ -0,0 +1,34 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern unsigned int fsync_alloc_shm( int low, int high ); ++extern void fsync_wake_futex( unsigned int shm_idx ); ++extern void fsync_clear_futex( unsigned int shm_idx ); ++extern void fsync_wake_up( struct object *obj ); ++extern void fsync_clear( struct object *obj ); ++ ++struct fsync; ++ ++extern const struct object_ops fsync_ops; ++extern void fsync_set_event( struct fsync *fsync ); ++extern void fsync_reset_event( struct fsync *fsync ); ++extern void fsync_abandon_mutexes( struct thread *thread ); +diff --git a/server/handle.c b/server/handle.c +index cb5628b7e06..ff44446acc9 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 61b5014c442..379f9c074d5 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index 18fef4b0466..b541524c9a4 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 3e02cbb3832..7a09436b637 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -37,6 +37,7 @@ + #include "thread.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + /* command-line options */ + int debug_level = 0; +@@ -141,8 +142,14 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_fsync()) ++ fsync_init(); ++ + if (do_esync()) + esync_init(); ++ ++ if (!do_fsync() && !do_esync()) ++ fprintf( stderr, "wineserver: using server-side synchronization.\n" ); + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); +diff --git a/server/mapping.c b/server/mapping.c +index 10def3ca694..d55df5145f7 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index 1235ab4731f..ccdc383f0df 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index e59a5b6c183..d0ea32a24d5 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 5b6bb9cbfe1..4c92b174ef3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -70,6 +70,8 @@ struct object_ops + int (*signaled)(struct object *,struct wait_queue_entry *); + /* return the esync fd for this object */ + int (*get_esync_fd)(struct object *, enum esync_type *type); ++ /* return the fsync shm idx for this object */ ++ unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index df50955f621..c4f51dcad30 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -50,6 +50,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + /* process object */ + +@@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -81,6 +83,7 @@ static const struct object_ops process_ops = + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ + process_get_esync_fd, /* get_esync_fd */ ++ process_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -180,6 +184,7 @@ static const struct object_ops job_ops = + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -547,6 +552,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + process->esync_fd = -1; ++ process->fsync_idx = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -603,6 +609,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_fsync()) ++ process->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + process->esync_fd = esync_create_fd( 0, 0 ); + +@@ -685,6 +694,13 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) + return process->esync_fd; + } + ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return process->fsync_idx; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index eec69ddbcaf..abb4ed31b48 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -99,6 +99,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ ++ unsigned int fsync_idx; + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 2a8662354f6..381bd8d8218 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3766,3 +3766,57 @@ enum esync_type + /* Retrieve the fd to wait on for user APCs. */ + @REQ(get_esync_apc_fd) + @END ++ ++enum fsync_type ++{ ++ FSYNC_SEMAPHORE = 1, ++ FSYNC_AUTO_EVENT, ++ FSYNC_MANUAL_EVENT, ++ FSYNC_MUTEX, ++ FSYNC_AUTO_SERVER, ++ FSYNC_MANUAL_SERVER, ++ FSYNC_QUEUE, ++}; ++ ++/* Create a new futex-based synchronization object */ ++@REQ(create_fsync) ++ unsigned int access; /* wanted access rights */ ++ int low; /* initial value of low word */ ++ int high; /* initial value of high word */ ++ int type; /* type of fsync object */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Open an fsync object */ ++@REQ(open_fsync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of fsync object */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the shm index for an object. */ ++@REQ(get_fsync_idx) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++@REQ(fsync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++@REQ(get_fsync_apc_idx) ++@REPLY ++ unsigned int shm_idx; ++@END +diff --git a/server/queue.c b/server/queue.c +index a78748b96ca..dccf43197cf 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "user.h" + #include "esync.h" ++#include "fsync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -147,6 +148,8 @@ struct msg_queue + unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ + int esync_fd; /* esync file descriptor (signalled on message) */ + int esync_in_msgwait; /* our thread is currently waiting on us */ ++ unsigned int fsync_idx; ++ int fsync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -164,6 +167,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -180,6 +184,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ + msg_queue_get_esync_fd, /* get_esync_fd */ ++ msg_queue_get_fsync_idx, /* get_fsync_idx */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops thread_input_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -321,12 +327,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->ignore_post_msg = 0; + queue->esync_fd = -1; + queue->esync_in_msgwait = 0; ++ queue->fsync_idx = 0; ++ queue->fsync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); + ++ if (do_fsync()) ++ queue->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + queue->esync_fd = esync_create_fd( 0, 0 ); + +@@ -509,6 +520,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + queue->keystate_lock = 0; + } + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -970,6 +984,9 @@ static int is_queue_hung( struct msg_queue *queue ) + return 0; /* thread is waiting on queue -> not hung */ + } + ++ if (do_fsync() && queue->fsync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + +@@ -1035,6 +1052,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) + return queue->esync_fd; + } + ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = FSYNC_QUEUE; ++ return queue->fsync_idx; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2517,6 +2541,9 @@ DECL_HANDLER(get_queue_status) + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -3536,3 +3563,18 @@ DECL_HANDLER(esync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fsync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->fsync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -168,6 +168,7 @@ static const struct object_ops key_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 20b0ec309f3..9fd08139375 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index d7d3a24e48f..604721d0d5f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index a50ace9903f..78d33460892 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -93,6 +93,7 @@ static const struct object_ops serial_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index b6d6dcfc4b6..f5ac61b6975 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -68,6 +68,7 @@ static const struct object_ops handler_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index c2dfa8fb8ce..080efc98942 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -173,6 +173,7 @@ static const struct object_ops sock_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1175,6 +1176,7 @@ static const struct object_ops ifchange_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1396,6 +1398,7 @@ static const struct object_ops socket_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 07f3c924f25..0e3e9ee9864 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 1a245c58396..1fb3603f2a7 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -52,6 +52,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + + #ifdef __i386__ +@@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -150,6 +152,7 @@ static const struct object_ops context_ops = + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -173,6 +176,7 @@ + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -187,6 +191,7 @@ static const struct object_ops thread_ops = + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ + thread_get_esync_fd, /* get_esync_fd */ ++ thread_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -228,6 +233,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->entry_point = 0; + thread->esync_fd = -1; + thread->esync_apc_fd = -1; ++ thread->fsync_idx = 0; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_fsync()) ++ { ++ thread->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); ++ } ++ + if (do_esync()) + { + thread->esync_fd = esync_create_fd( 0, 0 ); +@@ -484,6 +496,13 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) + return thread->esync_fd; + } + ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return thread->fsync_idx; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -533,6 +552,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca + apc->result.type = APC_NONE; + if (owner) grab_object( owner ); + } ++ + return apc; + } + +@@ -1068,6 +1088,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_fsync()) ++ fsync_wake_up( obj ); ++ + if (do_esync()) + esync_wake_up( obj ); + +@@ -1158,6 +1181,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + { + wake_thread( thread ); + ++ if (do_fsync() && queue == &thread->user_apc) ++ fsync_wake_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); + } +@@ -1208,6 +1234,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + list_remove( ptr ); + } + ++ if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ fsync_clear_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) + esync_clear( thread->esync_apc_fd ); + +@@ -1327,6 +1356,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_fsync()) ++ fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); + if (violent_death) +diff --git a/server/thread.h b/server/thread.h +index 0f6108b684a..53627631343 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -56,6 +56,8 @@ struct thread + struct list mutex_list; /* list of currently owned mutexes */ + int esync_fd; /* esync file descriptor (signalled on exit) */ + int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ ++ unsigned int fsync_idx; ++ unsigned int fsync_apc_idx; + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index dcbc9e2ece5..9d9d7b8b40c 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -37,6 +37,7 @@ + #include "handle.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -50,11 +51,13 @@ struct timer + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -69,6 +72,7 @@ static const struct object_ops timer_ops = + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ + timer_get_esync_fd, /* get_esync_fd */ ++ timer_get_fsync_idx, /* get_fsync_idx */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->thread = NULL; + timer->esync_fd = -1; + ++ if (do_fsync()) ++ timer->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + timer->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &timer->obj ); ++ + if (do_esync()) + esync_clear( timer->esync_fd ); + } +@@ -223,6 +233,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) + return timer->esync_fd; + } + ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; ++ return timer->fsync_idx; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 0f128728b0f..5a80b6fd50e 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -148,6 +148,7 @@ static const struct object_ops token_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 1a031248a7c..388b116bc67 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -113,6 +113,7 @@ static const struct object_ops window_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From f072cf6366aa8385efdcb4a7b4c49e122f00b750 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Mon, 14 Feb 2022 12:51:27 -0500 +Subject: [PATCH] fsync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/fsync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 39d969f061d..d468782667a 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -576,6 +576,9 @@ NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != FSYNC_MANUAL_EVENT && obj->type != FSYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + +From 69afcb164ccf8d3ecd5e94cf79c1e31698e14e5c Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Wed, 2 Feb 2022 17:02:44 -0500 +Subject: [PATCH] esync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/esync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 55c5695964d..4663374653a 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -526,6 +526,9 @@ NTSTATUS esync_set_event( HANDLE handle ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (obj->type == ESYNC_MANUAL_EVENT) + { + /* Acquire the spinlock. */ +diff --git a/server/queue.c b/server/queue.c +index 012795e410d..ba442324276 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -2536,6 +2536,8 @@ DECL_HANDLER(set_queue_mask) + if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; + else wake_up( &queue->obj, 0 ); + } ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); +@@ -2807,6 +2809,9 @@ DECL_HANDLER(get_message) + queue->changed_mask = req->changed_mask; + set_error( STATUS_PENDING ); /* FIXME */ + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-d5f2344.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-d5f2344.patch new file mode 100644 index 000000000..478399ae3 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync-unix-staging-d5f2344.patch @@ -0,0 +1,3763 @@ +From 1d3dadfd7707ac176b67b50a0dc573f799778836 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Fri, 16 Oct 2020 20:53:31 +0200 +Subject: Fsync rebased 5.13+ staging + + +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index c96a62ae006..a9be561d87a 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -48,6 +48,7 @@ C_SRCS = \ + unix/env.c \ + unix/esync.c \ + unix/file.c \ ++ unix/fsync.c \ + unix/loader.c \ + unix/process.c \ + unix/registry.c \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index ed801c71991..ac0326b1aba 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -57,6 +57,7 @@ + + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(esync); + +@@ -66,7 +67,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -867,7 +868,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + return ret; + } + +- if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) ++ if (count && objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE) + msgwait = TRUE; + + if (has_esync && has_server) +@@ -896,7 +897,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + } + } + +- if (wait_any || count == 1) ++ if (wait_any || count <= 1) + { + /* Try to check objects now, so we can obviate poll() at least. */ + for (i = 0; i < count; i++) +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +new file mode 100644 +index 00000000000..5012425b95a +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.c +@@ -0,0 +1,1267 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#if 0 ++#pragma makedep unix ++#endif ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#define NONAMELESSUNION ++#include "windef.h" ++#include "winternl.h" ++#include "wine/debug.h" ++#include "wine/server.h" ++ ++#include "unix_private.h" ++#include "fsync.h" ++ ++WINE_DEFAULT_DEBUG_CHANNEL(fsync); ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++ int bitset; ++}; ++#include "poppack.h" ++ ++static inline void small_pause(void) ++{ ++#if defined(__i386__) || defined(__x86_64__) ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++} ++ ++static unsigned int spincount = 100; ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ if (getenv("WINEFSYNC_SPINCOUNT")) ++ spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); ++ } ++ ++ return do_fsync_cached; ++#else ++ static int once; ++ if (!once++) ++ FIXME("futexes not supported on this platform.\n"); ++ return 0; ++#endif ++} ++ ++struct fsync ++{ ++ enum fsync_type type; ++ void *shm; /* pointer to shm section */ ++}; ++ ++struct semaphore ++{ ++ int count; ++ int max; ++}; ++C_ASSERT(sizeof(struct semaphore) == 8); ++ ++struct event ++{ ++ int signaled; ++ int unused; ++}; ++C_ASSERT(sizeof(struct event) == 8); ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++C_ASSERT(sizeof(struct mutex) == 8); ++ ++static char shm_name[29]; ++static int shm_fd; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ void *ret; ++ ++ pthread_mutex_lock( &shm_addrs_mutex ); ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size); ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize); ++ ++ TRACE("Mapping page %d at %p.\n", entry, addr); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ ret = (void *)((unsigned long)shm_addrs[entry] + offset); ++ ++ pthread_mutex_unlock( &shm_addrs_mutex ); ++ ++ return ret; ++} ++ ++/* We'd like lookup to be fast. To that end, we use a static list indexed by handle. ++ * This is copied and adapted from the fd cache code. */ ++ ++#define FSYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct fsync)) ++#define FSYNC_LIST_ENTRIES 256 ++ ++static struct fsync *fsync_list[FSYNC_LIST_ENTRIES]; ++static struct fsync fsync_list_initial_block[FSYNC_LIST_BLOCK_SIZE]; ++ ++static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry ) ++{ ++ UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1; ++ *entry = idx / FSYNC_LIST_BLOCK_SIZE; ++ return idx % FSYNC_LIST_BLOCK_SIZE; ++} ++ ++static struct fsync *add_to_list( HANDLE handle, enum fsync_type type, void *shm ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES) ++ { ++ FIXME( "too many allocated handles, not caching %p\n", handle ); ++ return FALSE; ++ } ++ ++ if (!fsync_list[entry]) /* do we need to allocate a new block of entries? */ ++ { ++ if (!entry) fsync_list[0] = fsync_list_initial_block; ++ else ++ { ++ void *ptr = anon_mmap_alloc( FSYNC_LIST_BLOCK_SIZE * sizeof(struct fsync), ++ PROT_READ | PROT_WRITE ); ++ if (ptr == MAP_FAILED) return FALSE; ++ fsync_list[entry] = ptr; ++ } ++ } ++ ++ if (!__sync_val_compare_and_swap((int *)&fsync_list[entry][idx].type, 0, type )) ++ fsync_list[entry][idx].shm = shm; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++static struct fsync *get_cached_object( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ if (entry >= FSYNC_LIST_ENTRIES || !fsync_list[entry]) return NULL; ++ if (!fsync_list[entry][idx].type) return NULL; ++ ++ return &fsync_list[entry][idx]; ++} ++ ++/* Gets an object. This is either a proper fsync object (i.e. an event, ++ * semaphore, etc. created using create_fsync) or a generic synchronizable ++ * server-side object which the server will signal (e.g. a process, thread, ++ * message queue, etc.) */ ++static NTSTATUS get_object( HANDLE handle, struct fsync **obj ) ++{ ++ NTSTATUS ret = STATUS_SUCCESS; ++ unsigned int shm_idx = 0; ++ enum fsync_type type; ++ ++ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS; ++ ++ if ((INT_PTR)handle < 0) ++ { ++ /* We can deal with pseudo-handles, but it's just easier this way */ ++ return STATUS_NOT_IMPLEMENTED; ++ } ++ ++ /* We need to try grabbing it from the server. */ ++ SERVER_START_REQ( get_fsync_idx ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (ret) ++ { ++ WARN("Failed to retrieve shm index for handle %p, status %#x.\n", handle, ret); ++ *obj = NULL; ++ return ret; ++ } ++ ++ TRACE("Got shm index %d for handle %p.\n", shm_idx, handle); ++ ++ *obj = add_to_list( handle, type, get_shm( shm_idx ) ); ++ return ret; ++} ++ ++NTSTATUS fsync_close( HANDLE handle ) ++{ ++ UINT_PTR entry, idx = handle_to_index( handle, &entry ); ++ ++ TRACE("%p.\n", handle); ++ ++ if (entry < FSYNC_LIST_ENTRIES && fsync_list[entry]) ++ { ++ if (__atomic_exchange_n( &fsync_list[entry][idx].type, 0, __ATOMIC_SEQ_CST )) ++ return STATUS_SUCCESS; ++ } ++ ++ return STATUS_INVALID_HANDLE; ++} ++ ++static NTSTATUS create_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int low, int high ) ++{ ++ NTSTATUS ret; ++ data_size_t len; ++ struct object_attributes *objattr; ++ unsigned int shm_idx; ++ ++ if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; ++ ++ SERVER_START_REQ( create_fsync ) ++ { ++ req->access = access; ++ req->low = low; ++ req->high = high; ++ req->type = type; ++ wine_server_add_data( req, objattr, len ); ++ ret = wine_server_call( req ); ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ shm_idx = reply->shm_idx; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx )); ++ TRACE("-> handle %p, shm index %d.\n", *handle, shm_idx); ++ } ++ ++ free( objattr ); ++ return ret; ++} ++ ++static NTSTATUS open_fsync( enum fsync_type type, HANDLE *handle, ++ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) ++{ ++ NTSTATUS ret; ++ unsigned int shm_idx; ++ ++ SERVER_START_REQ( open_fsync ) ++ { ++ req->access = access; ++ req->attributes = attr->Attributes; ++ req->rootdir = wine_server_obj_handle( attr->RootDirectory ); ++ req->type = type; ++ if (attr->ObjectName) ++ wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); ++ if (!(ret = wine_server_call( req ))) ++ { ++ *handle = wine_server_ptr_handle( reply->handle ); ++ type = reply->type; ++ shm_idx = reply->shm_idx; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (!ret) ++ { ++ add_to_list( *handle, type, get_shm( shm_idx ) ); ++ ++ TRACE("-> handle %p, shm index %u.\n", *handle, shm_idx); ++ } ++ return ret; ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (!do_fsync()) ++ { ++ /* make sure the server isn't running with WINEFSYNC */ ++ HANDLE handle; ++ NTSTATUS ret; ++ ++ ret = create_fsync( 0, &handle, 0, NULL, 0, 0 ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ { ++ ERR("Server is running with WINEFSYNC but this process is not, please enable WINEFSYNC or restart wineserver.\n"); ++ exit(1); ++ } ++ ++ return; ++ } ++ ++ if (stat( config_dir, &st ) == -1) ++ ERR("Cannot stat %s\n", config_dir); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1) ++ { ++ /* probably the server isn't running with WINEFSYNC, tell the user and bail */ ++ if (errno == ENOENT) ++ ERR("Failed to open fsync shared memory file; make sure no stale wineserver instances are running without WINEFSYNC.\n"); ++ else ++ ERR("Failed to initialize shared memory: %s\n", strerror( errno )); ++ exit(1); ++ } ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++} ++ ++NTSTATUS fsync_create_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max ) ++{ ++ TRACE("name %s, initial %d, max %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial, max); ++ ++ return create_fsync( FSYNC_SEMAPHORE, handle, access, attr, initial, max ); ++} ++ ++NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_SEMAPHORE, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ ULONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p, %d, %p.\n", handle, count, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ do ++ { ++ current = semaphore->count; ++ if (count + current > semaphore->max) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ } while (__sync_val_compare_and_swap( &semaphore->count, current, count + current ) != current); ++ ++ if (prev) *prev = current; ++ ++ futex_wake( &semaphore->count, INT_MAX ); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct semaphore *semaphore; ++ SEMAPHORE_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ semaphore = obj->shm; ++ ++ out->CurrentCount = semaphore->count; ++ out->MaximumCount = semaphore->max; ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial ) ++{ ++ enum fsync_type type = (event_type == SynchronizationEvent ? FSYNC_AUTO_EVENT : FSYNC_MANUAL_EVENT); ++ ++ TRACE("name %s, %s-reset, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", ++ event_type == NotificationEvent ? "manual" : "auto", initial); ++ ++ return create_fsync( type, handle, access, attr, initial, 0xdeadbeef ); ++} ++ ++NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_AUTO_EVENT, handle, access, attr ); ++} ++ ++NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ LONG current; ++ NTSTATUS ret; ++ ++ TRACE("%p.\n", handle); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ /* This isn't really correct; an application could miss the write. ++ * Unfortunately we can't really do much better. Fortunately this is rarely ++ * used (and publicly deprecated). */ ++ if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) ++ futex_wake( &event->signaled, INT_MAX ); ++ ++ /* Try to give other threads a chance to wake up. Hopefully erring on this ++ * side is the better thing to do... */ ++ usleep(0); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++ ++ if (prev) *prev = current; ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct event *event; ++ struct fsync *obj; ++ EVENT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ event = obj->shm; ++ ++ out->EventState = event->signaled; ++ out->EventType = (obj->type == FSYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) ++{ ++ TRACE("name %s, initial %d.\n", ++ attr ? debugstr_us(attr->ObjectName) : "", initial); ++ ++ return create_fsync( FSYNC_MUTEX, handle, access, attr, ++ initial ? GetCurrentThreadId() : 0, initial ? 1 : 0 ); ++} ++ ++NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) ++{ ++ TRACE("name %s.\n", debugstr_us(attr->ObjectName)); ++ ++ return open_fsync( FSYNC_MUTEX, handle, access, attr ); ++} ++ ++NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) ++{ ++ struct mutex *mutex; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ TRACE("%p, %p.\n", handle, prev); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED; ++ ++ if (prev) *prev = mutex->count; ++ ++ if (!--mutex->count) ++ { ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ ++ return STATUS_SUCCESS; ++} ++ ++NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) ++{ ++ struct fsync *obj; ++ struct mutex *mutex; ++ MUTANT_BASIC_INFORMATION *out = info; ++ NTSTATUS ret; ++ ++ TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len); ++ ++ if ((ret = get_object( handle, &obj ))) return ret; ++ mutex = obj->shm; ++ ++ out->CurrentCount = 1 - mutex->count; ++ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId()); ++ out->AbandonedState = (mutex->tid == ~0); ++ if (ret_len) *ret_len = sizeof(*out); ++ ++ return STATUS_SUCCESS; ++} ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; ++} ++ ++static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) ++{ ++ int ret; ++ ++ if (alertable) ++ { ++ int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; ++ struct futex_wait_block futexes[2]; ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ ++ futexes[0].addr = addr; ++ futexes[0].val = val; ++ futexes[1].addr = apc_futex; ++ futexes[1].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[0].pad = futexes[1].pad = 0; ++#endif ++ futexes[0].bitset = futexes[1].bitset = ~0; ++ ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait_multiple( futexes, 2, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, 2, NULL ); ++ ++ if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) ++ return STATUS_USER_APC; ++ } ++ else ++ { ++ if (end) ++ { ++ LONGLONG timeleft = update_timeout( *end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ret = futex_wait( addr, val, &tmo_p ); ++ } ++ else ++ ret = futex_wait( addr, val, NULL ); ++ } ++ ++ if (!ret) ++ return 0; ++ else if (ret < 0 && errno == ETIMEDOUT) ++ return STATUS_TIMEOUT; ++ else ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ static const LARGE_INTEGER zero = {0}; ++ ++ struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ int has_fsync = 0, has_server = 0; ++ BOOL msgwait = FALSE; ++ int dummy_futex = 0; ++ unsigned int spin; ++ LONGLONG timeleft; ++ LARGE_INTEGER now; ++ DWORD waitcount; ++ ULONGLONG end; ++ int i, ret; ++ ++ /* Grab the APC futex if we don't already have it. */ ++ if (alertable && !ntdll_get_thread_data()->fsync_apc_futex) ++ { ++ unsigned int idx = 0; ++ SERVER_START_REQ( get_fsync_apc_idx ) ++ { ++ if (!(ret = wine_server_call( req ))) ++ idx = reply->shm_idx; ++ } ++ SERVER_END_REQ; ++ ++ if (idx) ++ { ++ struct event *apc_event = get_shm( idx ); ++ ntdll_get_thread_data()->fsync_apc_futex = &apc_event->signaled; ++ } ++ } ++ ++ NtQuerySystemTime( &now ); ++ if (timeout) ++ { ++ if (timeout->QuadPart == TIMEOUT_INFINITE) ++ timeout = NULL; ++ else if (timeout->QuadPart > 0) ++ end = timeout->QuadPart; ++ else ++ end = now.QuadPart - timeout->QuadPart; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ ret = get_object( handles[i], &objs[i] ); ++ if (ret == STATUS_SUCCESS) ++ has_fsync = 1; ++ else if (ret == STATUS_NOT_IMPLEMENTED) ++ has_server = 1; ++ else ++ return ret; ++ } ++ ++ if (count && objs[count - 1] && objs[count - 1]->type == FSYNC_QUEUE) ++ msgwait = TRUE; ++ ++ if (has_fsync && has_server) ++ FIXME("Can't wait on fsync and server objects at the same time!\n"); ++ else if (has_server) ++ return STATUS_NOT_IMPLEMENTED; ++ ++ if (TRACE_ON(fsync)) ++ { ++ TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count); ++ for (i = 0; i < count; i++) ++ TRACE(" %p", handles[i]); ++ ++ if (msgwait) ++ TRACE(" or driver events"); ++ if (alertable) ++ TRACE(", alertable"); ++ ++ if (!timeout) ++ TRACE(", timeout = INFINITE.\n"); ++ else ++ { ++ timeleft = update_timeout( end ); ++ TRACE(", timeout = %ld.%07ld sec.\n", ++ (long) (timeleft / TICKSPERSEC), (long) (timeleft % TICKSPERSEC)); ++ } ++ } ++ ++ if (wait_any || count <= 1) ++ { ++ while (1) ++ { ++ /* Try to grab anything. */ ++ ++ if (alertable) ++ { ++ /* We must check this first! The server may set an event that ++ * we're waiting on, but we need to return STATUS_USER_APC. */ ++ if (__atomic_load_n( ntdll_get_thread_data()->fsync_apc_futex, __ATOMIC_SEQ_CST )) ++ goto userapc; ++ } ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj) ++ { ++ if (!obj->type) /* gcc complains if we put this in the switch */ ++ { ++ /* Someone probably closed an object while waiting on it. */ ++ WARN("Handle %p has type 0; was it closed?\n", handles[i]); ++ return STATUS_INVALID_HANDLE; ++ } ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ int current; ++ ++ /* It would be a little clearer (and less error-prone) ++ * to use a dedicated interlocked_dec_if_nonzero() ++ * helper, but nesting loops like that is probably not ++ * great for performance... */ ++ for (spin = 0; spin <= spincount || current; ++spin) ++ { ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &semaphore->count; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count++; ++ return i; ++ } ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &mutex->tid; ++ futexes[i].val = tid; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ case FSYNC_MANUAL_EVENT: ++ case FSYNC_MANUAL_SERVER: ++ case FSYNC_QUEUE: ++ { ++ struct event *event = obj->shm; ++ ++ for (spin = 0; spin <= spincount; ++spin) ++ { ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ return i; ++ } ++ small_pause(); ++ } ++ ++ futexes[i].addr = &event->signaled; ++ futexes[i].val = 0; ++ break; ++ } ++ default: ++ ERR("Invalid type %#x for handle %p.\n", obj->type, handles[i]); ++ assert(0); ++ } ++ } ++ else ++ { ++ /* Avoid breaking things entirely. */ ++ futexes[i].addr = &dummy_futex; ++ futexes[i].val = dummy_futex; ++ } ++ ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ } ++ ++ if (alertable) ++ { ++ /* We already checked if it was signaled; don't bother doing it again. */ ++ futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; ++ futexes[i].val = 0; ++#if __SIZEOF_POINTER__ == 4 ++ futexes[i].pad = 0; ++#endif ++ futexes[i].bitset = ~0; ++ i++; ++ } ++ waitcount = i; ++ ++ /* Looks like everything is contended, so wait. */ ++ ++ if (timeout && !timeout->QuadPart) ++ { ++ /* Unlike esync, we already know that we've timed out, so we ++ * can avoid a syscall. */ ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ else if (timeout) ++ { ++ LONGLONG timeleft = update_timeout( end ); ++ struct timespec tmo_p; ++ tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; ++ tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; ++ ++ ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); ++ } ++ else ++ ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ++ /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, ++ * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to ++ * try again, bad address is already handled by the fact that we ++ * tried to read from it, so only break out on a timeout. */ ++ if (ret == -1 && errno == ETIMEDOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return STATUS_TIMEOUT; ++ } ++ } /* while (1) */ ++ } ++ else ++ { ++ /* Wait-all is a little trickier to implement correctly. Fortunately, ++ * it's not as common. ++ * ++ * The idea is basically just to wait in sequence on every object in the ++ * set. Then when we're done, try to grab them all in a tight loop. If ++ * that fails, release any resources we've grabbed (and yes, we can ++ * reliably do this—it's just mutexes and semaphores that we have to ++ * put back, and in both cases we just put back 1), and if any of that ++ * fails we start over. ++ * ++ * What makes this inherently bad is that we might temporarily grab a ++ * resource incorrectly. Hopefully it'll be quick (and hey, it won't ++ * block on wineserver) so nobody will notice. Besides, consider: if ++ * object A becomes signaled but someone grabs it before we can grab it ++ * and everything else, then they could just as well have grabbed it ++ * before it became signaled. Similarly if object A was signaled and we ++ * were blocking on object B, then B becomes available and someone grabs ++ * A before we can, then they might have grabbed A before B became ++ * signaled. In either case anyone who tries to wait on A or B will be ++ * waiting for an instant while we put things back. */ ++ ++ NTSTATUS status = STATUS_SUCCESS; ++ int current; ++ ++ while (1) ++ { ++ BOOL abandoned; ++ ++tryagain: ++ abandoned = FALSE; ++ ++ /* First step: try to wait on each object in sequence. */ ++ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ ++ if (mutex->tid == GetCurrentThreadId()) ++ continue; ++ ++ while ((current = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ))) ++ { ++ status = do_single_wait( &mutex->tid, current, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ else if (obj) ++ { ++ /* this works for semaphores too */ ++ struct event *event = obj->shm; ++ ++ while (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ { ++ status = do_single_wait( &event->signaled, 0, timeout ? &end : NULL, alertable ); ++ if (status != STATUS_PENDING) ++ break; ++ } ++ } ++ ++ if (status == STATUS_TIMEOUT) ++ { ++ TRACE("Wait timed out.\n"); ++ return status; ++ } ++ else if (status == STATUS_USER_APC) ++ goto userapc; ++ } ++ ++ /* If we got here and we haven't timed out, that means all of the ++ * handles were signaled. Check to make sure they still are. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ ++ if (obj && obj->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ ++ if (tid && tid != ~0 && tid != GetCurrentThreadId()) ++ goto tryagain; ++ } ++ else if (obj) ++ { ++ struct event *event = obj->shm; ++ ++ if (!__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) ++ goto tryagain; ++ } ++ } ++ ++ /* Yep, still signaled. Now quick, grab everything. */ ++ for (i = 0; i < count; i++) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ int tid = __atomic_load_n( &mutex->tid, __ATOMIC_SEQ_CST ); ++ if (tid == GetCurrentThreadId()) ++ break; ++ if (tid && tid != ~0) ++ goto tooslow; ++ if (__sync_val_compare_and_swap( &mutex->tid, tid, GetCurrentThreadId() ) != tid) ++ goto tooslow; ++ if (tid == ~0) ++ abandoned = TRUE; ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ goto tooslow; ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ if (!__sync_val_compare_and_swap( &event->signaled, 1, 0 )) ++ goto tooslow; ++ break; ++ } ++ default: ++ /* If a manual-reset event changed between there and ++ * here, it's shouldn't be a problem. */ ++ break; ++ } ++ } ++ ++ /* If we got here, we successfully waited on every object. ++ * Make sure to let ourselves know that we grabbed the mutexes. */ ++ for (i = 0; i < count; i++) ++ { ++ if (objs[i] && objs[i]->type == FSYNC_MUTEX) ++ { ++ struct mutex *mutex = objs[i]->shm; ++ mutex->count++; ++ } ++ } ++ ++ if (abandoned) ++ { ++ TRACE("Wait successful, but some object(s) were abandoned.\n"); ++ return STATUS_ABANDONED; ++ } ++ TRACE("Wait successful.\n"); ++ return STATUS_SUCCESS; ++ ++tooslow: ++ for (--i; i >= 0; i--) ++ { ++ struct fsync *obj = objs[i]; ++ switch (obj->type) ++ { ++ case FSYNC_MUTEX: ++ { ++ struct mutex *mutex = obj->shm; ++ /* HACK: This won't do the right thing with abandoned ++ * mutexes, but fixing it is probably more trouble than ++ * it's worth. */ ++ __atomic_store_n( &mutex->tid, 0, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ case FSYNC_SEMAPHORE: ++ { ++ struct semaphore *semaphore = obj->shm; ++ __sync_fetch_and_add( &semaphore->count, 1 ); ++ break; ++ } ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_AUTO_SERVER: ++ { ++ struct event *event = obj->shm; ++ __atomic_store_n( &event->signaled, 1, __ATOMIC_SEQ_CST ); ++ break; ++ } ++ default: ++ /* doesn't need to be put back */ ++ break; ++ } ++ } ++ } /* while (1) */ ++ } /* else (wait-all) */ ++ ++ assert(0); /* shouldn't reach here... */ ++ ++userapc: ++ TRACE("Woken up by user APC.\n"); ++ ++ /* We have to make a server call anyway to get the APC to execute, so just ++ * delegate down to server_wait(). */ ++ ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero ); ++ ++ /* This can happen if we received a system APC, and the APC fd was woken up ++ * before we got SIGUSR1. poll() doesn't return EINTR in that case. The ++ * right thing to do seems to be to return STATUS_USER_APC anyway. */ ++ if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC; ++ return ret; ++} ++ ++/* Like esync, we need to let the server know when we are doing a message wait, ++ * and when we are done with one, so that all of the code surrounding hung ++ * queues works, and we also need this for WaitForInputIdle(). ++ * ++ * Unlike esync, we can't wait on the queue fd itself locally. Instead we let ++ * the server do that for us, the way it normally does. This could actually ++ * work for esync too, and that might be better. */ ++static void server_set_msgwait( int in_msgwait ) ++{ ++ SERVER_START_REQ( fsync_msgwait ) ++ { ++ req->in_msgwait = in_msgwait; ++ wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++} ++ ++/* This is a very thin wrapper around the proper implementation above. The ++ * purpose is to make sure the server knows when we are doing a message wait. ++ * This is separated into a wrapper function since there are at least a dozen ++ * exit paths from fsync_wait_objects(). */ ++NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ BOOL msgwait = FALSE; ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if (count && !get_object( handles[count - 1], &obj ) && obj->type == FSYNC_QUEUE) ++ { ++ msgwait = TRUE; ++ server_set_msgwait( 1 ); ++ } ++ ++ ret = __fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ ++ if (msgwait) ++ server_set_msgwait( 0 ); ++ ++ return ret; ++} ++ ++NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, ++ const LARGE_INTEGER *timeout ) ++{ ++ struct fsync *obj; ++ NTSTATUS ret; ++ ++ if ((ret = get_object( signal, &obj ))) return ret; ++ ++ switch (obj->type) ++ { ++ case FSYNC_SEMAPHORE: ++ ret = fsync_release_semaphore( signal, 1, NULL ); ++ break; ++ case FSYNC_AUTO_EVENT: ++ case FSYNC_MANUAL_EVENT: ++ ret = fsync_set_event( signal, NULL ); ++ break; ++ case FSYNC_MUTEX: ++ ret = fsync_release_mutex( signal, NULL ); ++ break; ++ default: ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ } ++ if (ret) return ret; ++ ++ return fsync_wait_objects( 1, &wait, TRUE, alertable, timeout ); ++} +diff --git a/dlls/ntdll/unix/fsync.h b/dlls/ntdll/unix/fsync.h +new file mode 100644 +index 00000000000..b3604548554 +--- /dev/null ++++ b/dlls/ntdll/unix/fsync.h +@@ -0,0 +1,49 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void) DECLSPEC_HIDDEN; ++extern void fsync_init(void) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_close( HANDLE handle ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_create_semaphore(HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_semaphore( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_event( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_event( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_create_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_open_mutex( HANDLE *handle, ACCESS_MASK access, ++ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_release_mutex( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) DECLSPEC_HIDDEN; ++ ++extern NTSTATUS fsync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; ++extern NTSTATUS fsync_signal_and_wait( HANDLE signal, HANDLE wait, ++ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 19b73256ef2..d0d979248f8 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -87,6 +87,7 @@ + #include "winternl.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "wine/list.h" + #include "wine/debug.h" + +@@ -1565,6 +1566,7 @@ static void start_main_thread(void) + signal_init_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ fsync_init(); + esync_init(); + virtual_map_user_shared_data(); + init_cpu_info(); +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 34d4c80eee5..c48a6339fee 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -95,6 +95,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + #include "ddk/wdm.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); +@@ -1698,6 +1699,9 @@ NTSTATUS WINAPI NtClose( HANDLE handle ) + NTSTATUS ret; + int fd = remove_fd_from_cache( handle ); + ++ if (do_fsync()) ++ fsync_close( handle ); ++ + if (do_esync()) + esync_close( handle ); + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 051c672bd12..d8a8eb526a1 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -73,6 +73,7 @@ + #include "wine/debug.h" + #include "unix_private.h" + #include "esync.h" ++#include "fsync.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); + +@@ -262,6 +262,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + ++ if (do_fsync()) ++ return fsync_create_semaphore( handle, access, attr, initial, max ); ++ + if (do_esync()) + return esync_create_semaphore( handle, access, attr, initial, max ); + +@@ -297,6 +297,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_open_semaphore( handle, access, attr ); ++ + if (do_esync()) + return esync_open_semaphore( handle, access, attr ); + +@@ -327,6 +327,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla + + if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_semaphore( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_semaphore( handle, info, ret_len ); + +@@ -367,6 +367,9 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_semaphore( handle, count, previous ); ++ + if (do_esync()) + return esync_release_semaphore( handle, count, previous ); + +@@ -394,6 +394,9 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ + *handle = 0; + if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER; + ++ if (do_fsync()) ++ return fsync_create_event( handle, access, attr, type, state ); ++ + if (do_esync()) + return esync_create_event( handle, access, attr, type, state ); + +@@ -428,6 +428,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_event( handle, access, attr ); ++ + if (do_esync()) + return esync_open_event( handle, access, attr ); + +@@ -458,6 +458,9 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_set_event( handle, prev_state ); ++ + if (do_esync()) + return esync_set_event( handle ); + +@@ -480,6 +480,9 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + /* This comment is a dummy to make sure this patch applies in the right place. */ + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_reset_event( handle, prev_state ); ++ + if (do_esync()) + return esync_reset_event( handle ); + +@@ -512,6 +512,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_pulse_event( handle, prev_state ); ++ + if (do_esync()) + return esync_pulse_event( handle ); + +@@ -537,6 +537,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class, + + if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_event( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_event( handle, info, ret_len ); + +@@ -578,6 +578,9 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT + + *handle = 0; + ++ if (do_fsync()) ++ return fsync_create_mutex( handle, access, attr, owned ); ++ + if (do_esync()) + return esync_create_mutex( handle, access, attr, owned ); + +@@ -611,6 +611,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + + if ((ret = validate_open_object_attributes( attr ))) return ret; + ++ if (do_fsync()) ++ return fsync_open_mutex( handle, access, attr ); ++ + if (do_esync()) + return esync_open_mutex( handle, access, attr ); + +@@ -641,6 +641,9 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ if (do_fsync()) ++ return fsync_release_mutex( handle, prev_count ); ++ + if (do_esync()) + return esync_release_mutex( handle, prev_count ); + +@@ -665,6 +665,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class, + + if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + ++ if (do_fsync()) ++ return fsync_query_mutex( handle, info, ret_len ); ++ + if (do_esync()) + return esync_query_mutex( handle, info, ret_len ); + +@@ -1286,6 +1286,13 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + + if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( count, handles, wait_any, alertable, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ + if (do_esync()) + { + NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout ); +@@ -1323,6 +1323,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + ++ if (do_fsync()) ++ return fsync_signal_and_wait( signal, wait, alertable, timeout ); ++ + if (do_esync()) + return esync_signal_and_wait( signal, wait, alertable, timeout ); + +@@ -1348,7 +1348,24 @@ NTSTATUS WINAPI NtYieldExecution(void) + NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) + { + /* if alertable, we need to query the server */ +- if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ if (alertable) ++ { ++ if (do_fsync()) ++ { ++ NTSTATUS ret = fsync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ if (do_esync()) ++ { ++ NTSTATUS ret = esync_wait_objects( 0, NULL, TRUE, TRUE, timeout ); ++ if (ret != STATUS_NOT_IMPLEMENTED) ++ return ret; ++ } ++ ++ return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); ++ } + + if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ + { +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index e7033932bcb..a2f485d892f 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -59,6 +59,7 @@ struct ntdll_thread_data + struct debug_info *debug_info; /* info for debugstr functions */ + void *start_stack; /* stack for thread startup */ + int esync_apc_fd; /* fd to wait on for user APCs */ ++ int *fsync_apc_futex; + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index bd89649e769..608c83b579c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2683,6 +2683,7 @@ static void init_teb( TEB *teb, PEB *peb ) + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data->esync_apc_fd = -1; ++ thread_data->fsync_apc_futex = NULL; + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; +diff --git a/server/Makefile.in b/server/Makefile.in +index 8bd612b4728..6dc5c465e52 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -15,6 +15,7 @@ C_SRCS = \ + event.c \ + fd.c \ + file.c \ ++ fsync.c \ + handle.c \ + hook.c \ + mach.c \ +diff --git a/server/async.c b/server/async.c +index 1b4f86a1b8b..ceff9c75c27 100644 +--- a/server/async.c ++++ b/server/async.c +@@ -71,6 +71,7 @@ static const struct object_ops async_ops = + remove_queue, /* remove_queue */ + async_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + async_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -486,6 +487,7 @@ static const struct object_ops iosb_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/atom.c b/server/atom.c +index 73d858fef82..7acef97c30d 100644 +--- a/server/atom.c ++++ b/server/atom.c +@@ -81,6 +81,7 @@ static const struct object_ops atom_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/change.c b/server/change.c +index 85afb0cbdc5..7eea72757c5 100644 +--- a/server/change.c ++++ b/server/change.c +@@ -116,6 +116,7 @@ static const struct object_ops dir_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + dir_get_fd, /* get_fd */ +diff --git a/server/clipboard.c b/server/clipboard.c +index 54a5fb683cc..1fa61b67a88 100644 +--- a/server/clipboard.c ++++ b/server/clipboard.c +@@ -78,6 +78,7 @@ static const struct object_ops clipboard_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/completion.c b/server/completion.c +index ef66260c991..2149d0bbf32 100644 +--- a/server/completion.c ++++ b/server/completion.c +@@ -65,6 +65,7 @@ static const struct object_ops completion_ops = + remove_queue, /* remove_queue */ + completion_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/console.c b/server/console.c +index 78635f3fae1..824b930bba0 100644 +--- a/server/console.c ++++ b/server/console.c +@@ -43,6 +43,7 @@ + #include "winternl.h" + #include "wine/condrv.h" + #include "esync.h" ++#include "fsync.h" + + struct screen_buffer; + +@@ -82,6 +83,7 @@ static const struct object_ops console_input_ops = + remove_queue, /* remove_queue */ + console_input_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_input_get_fd, /* get_fd */ +@@ -136,6 +138,7 @@ struct console_server + int term_fd; /* UNIX terminal fd */ + struct termios termios; /* original termios */ + int esync_fd; ++ unsigned int fsync_idx; + }; + + static void console_server_dump( struct object *obj, int verbose ); +@@ -146,6 +149,7 @@ static struct object *console_server_lookup_name( struct object *obj, struct uni + static void console_server_destroy( struct object *obj ); + static int console_server_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int console_server_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static struct fd *console_server_get_fd( struct object *obj ); + static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); + static struct object *console_server_open_file( struct object *obj, unsigned int access, +@@ -156,6 +160,7 @@ static const struct object_ops console_server_ops = + remove_queue, /* remove_queue */ + console_server_signaled, /* signaled */ + console_server_get_esync_fd, /* get_esync_fd */ ++ console_server_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_server_get_fd, /* get_fd */ +@@ -224,6 +229,7 @@ static const struct object_ops screen_buffer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + screen_buffer_get_fd, /* get_fd */ +@@ -272,6 +278,7 @@ static const struct object_ops console_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -297,6 +301,7 @@ static const struct object_ops input_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + input_device_get_fd, /* get_fd */ +@@ -327,6 +332,7 @@ static const struct object_ops output_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + output_device_get_fd, /* get_fd */ +@@ -365,6 +371,7 @@ static const struct object_ops console_connection_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + console_connection_get_fd, /* get_fd */ +@@ -587,8 +587,13 @@ static void disconnect_console_server( struct console_server *server ) + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + } ++ ++ if (do_fsync()) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync()) + esync_clear( server->esync_fd ); ++ + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); +@@ -774,6 +787,13 @@ static int console_server_get_esync_fd( struct object *obj, enum esync_type *typ + return server->esync_fd; + } + ++static unsigned int console_server_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct console_server *server = (struct console_server*)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return server->fsync_idx; ++} ++ + static struct fd *console_server_get_fd( struct object* obj ) + { + struct console_server *server = (struct console_server*)obj; +@@ -806,6 +828,9 @@ static struct object *create_console_server( void ) + } + allow_fd_caching(server->fd); + ++ if (do_fsync()) ++ server->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + server->esync_fd = esync_create_fd( 0, 0 ); + +@@ -1545,6 +1560,10 @@ DECL_HANDLER(get_next_console_request) + /* set result of previous ioctl */ + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + } +@@ -1645,6 +1664,10 @@ DECL_HANDLER(get_next_console_request) + { + set_error( STATUS_PENDING ); + } ++ ++ if (do_fsync() && list_empty( &server->queue )) ++ fsync_clear_futex( server->fsync_idx ); ++ + if (do_esync() && list_empty( &server->queue )) + esync_clear( server->esync_fd ); + +diff --git a/server/debugger.c b/server/debugger.c +index c37f97aa0b6..b7d88cfd2f2 100644 +--- a/server/debugger.c ++++ b/server/debugger.c +@@ -74,6 +74,7 @@ static const struct object_ops debug_event_ops = + remove_queue, /* remove_queue */ + debug_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -103,6 +104,7 @@ static const struct object_ops debug_ctx_ops = + remove_queue, /* remove_queue */ + debug_obj_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/device.c b/server/device.c +index b8ce131c732..c56151c9b18 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -40,6 +40,7 @@ + #include "request.h" + #include "process.h" + #include "esync.h" ++#include "fsync.h" + + /* IRP object */ + +@@ -70,6 +71,7 @@ static const struct object_ops irp_call_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -97,11 +99,13 @@ struct device_manager + struct irp_call *current_call; /* call currently executed on client side */ + struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void device_manager_dump( struct object *obj, int verbose ); + static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void device_manager_destroy( struct object *obj ); + + static const struct object_ops device_manager_ops = +@@ -113,6 +117,7 @@ static const struct object_ops device_manager_ops = + remove_queue, /* remove_queue */ + device_manager_signaled, /* signaled */ + device_manager_get_esync_fd, /* get_esync_fd */ ++ device_manager_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -158,6 +163,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -210,6 +216,7 @@ static const struct object_ops device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + device_file_get_fd, /* get_fd */ +@@ -754,6 +761,9 @@ static void delete_file( struct device_file *file ) + /* terminate all pending requests */ + LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry ) + { ++ if (do_fsync() && file->device->manager && list_empty( &file->device->manager->requests )) ++ fsync_clear( &file->device->manager->obj ); ++ + if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests )) + esync_clear( file->device->manager->esync_fd ); + +@@ -799,6 +809,13 @@ static int device_manager_get_esync_fd( struct object *obj, enum esync_type *typ + return manager->esync_fd; + } + ++static unsigned int device_manager_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct device_manager *manager = (struct device_manager *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return manager->fsync_idx; ++} ++ + static void device_manager_destroy( struct object *obj ) + { + struct device_manager *manager = (struct device_manager *)obj; +@@ -849,6 +866,9 @@ static struct device_manager *create_device_manager(void) + list_init( &manager->requests ); + wine_rb_init( &manager->kernel_objects, compare_kernel_object ); + ++ if (do_fsync()) ++ manager->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + manager->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -1017,6 +1037,9 @@ DECL_HANDLER(get_next_device_request) + if (irp->file) grab_object( irp ); + manager->current_call = irp; + ++ if (do_fsync() && list_empty( &manager->requests )) ++ fsync_clear( &manager->obj ); ++ + if (do_esync() && list_empty( &manager->requests )) + esync_clear( manager->esync_fd ); + } +diff --git a/server/directory.c b/server/directory.c +index 007ec1002ac..62f2e50916f 100644 +--- a/server/directory.c ++++ b/server/directory.c +@@ -59,6 +59,7 @@ static const struct object_ops object_type_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -99,6 +100,7 @@ static const struct object_ops directory_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/esync.c b/server/esync.c +index 5c641fdbe01..6395ba4378c 100644 +--- a/server/esync.c ++++ b/server/esync.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "file.h" + #include "esync.h" ++#include "fsync.h" + + int do_esync(void) + { +@@ -51,7 +52,7 @@ int do_esync(void) + static int do_esync_cached = -1; + + if (do_esync_cached == -1) +- do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")); ++ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC")) && !do_fsync(); + + return do_esync_cached; + #else +@@ -130,6 +131,7 @@ const struct object_ops esync_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + esync_get_esync_fd, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/event.c b/server/event.c +index 8607b494b6d..28a52e61d00 100644 +--- a/server/event.c ++++ b/server/event.c +@@ -36,6 +36,7 @@ + #include "request.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR event_name[] = {'E','v','e','n','t'}; + +@@ -44,6 +45,7 @@ struct event + int manual_reset; /* is it a manual reset event? */ + int signaled; /* event has been signaled */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; + }; + + static void event_dump( struct object *obj, int verbose ); +@@ -51,6 +53,7 @@ static void event_dump( struct object *obj, int verbose ); + static int event_signaled( struct object *obj, struct wait_queue_entry *entry ); + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static int event_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static int event_signal( struct object *obj, unsigned int access); + static struct list *event_get_kernel_obj_list( struct object *obj ); + static void event_destroy( struct object *obj ); +@@ -65,6 +68,7 @@ static const struct object_ops event_ops = + remove_queue, /* remove_queue */ + event_signaled, /* signaled */ + event_get_esync_fd, /* get_esync_fd */ ++ event_get_fsync_idx, /* get_fsync_idx */ + event_satisfied, /* satisfied */ + event_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -101,6 +105,7 @@ static const struct object_ops keyed_event_ops = + remove_queue, /* remove_queue */ + keyed_event_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +138,9 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + event->manual_reset = manual_reset; + event->signaled = initial_state; + ++ if (do_fsync()) ++ event->fsync_idx = fsync_alloc_shm( initial_state, 0 ); ++ + if (do_esync()) + event->esync_fd = esync_create_fd( initial_state, 0 ); + } +@@ -143,6 +151,10 @@ struct event *create_event( struct object *root, const struct unicode_str *name, + struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access ) + { + struct object *obj; ++ ++ if (do_fsync() && (obj = get_handle_obj( process, handle, access, &fsync_ops))) ++ return (struct event *)obj; /* even though it's not an event */ ++ + if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops))) + return (struct event *)obj; /* even though it's not an event */ + +@@ -155,10 +167,19 @@ void pulse_event( struct event *event ) + /* wake up all waiters if manual reset, a single one otherwise */ + wake_up( &event->obj, !event->manual_reset ); + event->signaled = 0; ++ ++ if (do_fsync()) ++ fsync_clear( &event->obj ); + } + + void set_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_set_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_set_event( (struct esync *)event ); +@@ -172,6 +193,12 @@ void set_event( struct event *event ) + + void reset_event( struct event *event ) + { ++ if (do_fsync() && event->obj.ops == &fsync_ops) ++ { ++ fsync_reset_event( (struct fsync *)event ); ++ return; ++ } ++ + if (do_esync() && event->obj.ops == &esync_ops) + { + esync_reset_event( (struct esync *)event ); +@@ -179,6 +206,9 @@ void reset_event( struct event *event ) + } + event->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &event->obj ); ++ + if (do_esync()) + esync_clear( event->esync_fd ); + } +@@ -211,6 +241,13 @@ static int event_get_esync_fd( struct object *obj, enum esync_type *type ) + return event->esync_fd; + } + ++static unsigned int event_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct event *event = (struct event *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return event->fsync_idx; ++} ++ + static void event_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct event *event = (struct event *)obj; +diff --git a/server/fd.c b/server/fd.c +index bbc2462163d..c558229c3c8 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -103,6 +103,7 @@ + #include "process.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + #include "winternl.h" + #include "winioctl.h" +@@ -205,6 +206,7 @@ struct fd + apc_param_t comp_key; /* completion key to set in completion events */ + unsigned int comp_flags; /* completion flags */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void fd_dump( struct object *obj, int verbose ); +@@ -219,6 +221,7 @@ static const struct object_ops fd_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -261,6 +264,7 @@ static const struct object_ops device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -302,6 +306,7 @@ static const struct object_ops inode_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -345,6 +350,7 @@ static const struct object_ops file_lock_ops = + remove_queue, /* remove_queue */ + file_lock_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -1714,6 +1720,7 @@ static struct fd *alloc_fd_object(void) + fd->completion = NULL; + fd->comp_flags = 0; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); +@@ -1723,6 +1730,9 @@ static struct fd *alloc_fd_object(void) + if (do_esync()) + fd->esync_fd = esync_create_fd( 1, 0 ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 1, 0 ); ++ + if ((fd->poll_index = add_poll_user( fd )) == -1) + { + release_object( fd ); +@@ -1756,14 +1766,19 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use + fd->comp_flags = 0; + fd->no_fd_status = STATUS_BAD_DEVICE_TYPE; + fd->esync_fd = -1; ++ fd->fsync_idx = 0; + init_async_queue( &fd->read_q ); + init_async_queue( &fd->write_q ); + init_async_queue( &fd->wait_q ); + list_init( &fd->inode_entry ); + list_init( &fd->locks ); + ++ if (do_fsync()) ++ fd->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + fd->esync_fd = esync_create_fd( 0, 0 ); ++ + return fd; + } + +@@ -2190,6 +2205,9 @@ void set_fd_signaled( struct fd *fd, int signaled ) + fd->signaled = signaled; + if (signaled) wake_up( fd->user, 0 ); + ++ if (do_fsync() && !signaled) ++ fsync_clear( fd->user ); ++ + if (do_esync() && !signaled) + esync_clear( fd->esync_fd ); + } +@@ -2232,6 +2250,15 @@ int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ) + return ret; + } + ++unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct fd *fd = get_obj_fd( obj ); ++ unsigned int ret = fd->fsync_idx; ++ *type = FSYNC_MANUAL_SERVER; ++ release_object( fd ); ++ return ret; ++} ++ + int default_fd_get_poll_events( struct fd *fd ) + { + int events = 0; +diff --git a/server/file.c b/server/file.c +index 49c4ba32b42..829c8685d63 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -112,6 +112,7 @@ static const struct object_ops file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + file_get_fd, /* get_fd */ +diff --git a/server/file.h b/server/file.h +index cae0ac1e395..ed030af8d8f 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -103,6 +103,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ); + + extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry ); + extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type ); ++extern unsigned int default_fd_get_fsync_idx( struct object *obj, enum fsync_type *type ); + extern int default_fd_get_poll_events( struct fd *fd ); + extern void default_poll_event( struct fd *fd, int event ); + extern void fd_queue_async( struct fd *fd, struct async *async, int type ); +diff --git a/server/fsync.c b/server/fsync.c +new file mode 100644 +index 00000000000..0a88ecdd3f6 +--- /dev/null ++++ b/server/fsync.c +@@ -0,0 +1,524 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef HAVE_SYS_SYSCALL_H ++# include ++#endif ++#include ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++ ++#include "handle.h" ++#include "request.h" ++#include "fsync.h" ++ ++#include "pshpack4.h" ++struct futex_wait_block ++{ ++ int *addr; ++#if __SIZEOF_POINTER__ == 4 ++ int pad; ++#endif ++ int val; ++}; ++#include "poppack.h" ++ ++static inline int futex_wait_multiple( const struct futex_wait_block *futexes, ++ int count, const struct timespec *timeout ) ++{ ++ return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++} ++ ++int do_fsync(void) ++{ ++#ifdef __linux__ ++ static int do_fsync_cached = -1; ++ ++ if (do_fsync_cached == -1) ++ { ++ static const struct timespec zero; ++ futex_wait_multiple( NULL, 0, &zero ); ++ do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; ++ } ++ ++ return do_fsync_cached; ++#else ++ return 0; ++#endif ++} ++ ++static char shm_name[29]; ++static int shm_fd; ++static off_t shm_size; ++static void **shm_addrs; ++static int shm_addrs_size; /* length of the allocated shm_addrs array */ ++static long pagesize; ++ ++static int is_fsync_initialized; ++ ++static void shm_cleanup(void) ++{ ++ close( shm_fd ); ++ if (shm_unlink( shm_name ) == -1) ++ perror( "shm_unlink" ); ++} ++ ++void fsync_init(void) ++{ ++ struct stat st; ++ ++ if (fstat( config_dir_fd, &st ) == -1) ++ fatal_error( "cannot stat config dir\n" ); ++ ++ if (st.st_ino != (unsigned long)st.st_ino) ++ sprintf( shm_name, "/wine-%lx%08lx-fsync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino ); ++ else ++ sprintf( shm_name, "/wine-%lx-fsync", (unsigned long)st.st_ino ); ++ ++ if (!shm_unlink( shm_name )) ++ fprintf( stderr, "fsync: warning: a previous shm file %s was not properly removed\n", shm_name ); ++ ++ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 ); ++ if (shm_fd == -1) ++ perror( "shm_open" ); ++ ++ pagesize = sysconf( _SC_PAGESIZE ); ++ ++ shm_addrs = calloc( 128, sizeof(shm_addrs[0]) ); ++ shm_addrs_size = 128; ++ ++ shm_size = pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ perror( "ftruncate" ); ++ ++ is_fsync_initialized = 1; ++ ++ fprintf( stderr, "fsync: up and running.\n" ); ++ ++ atexit( shm_cleanup ); ++} ++ ++static struct list mutex_list = LIST_INIT(mutex_list); ++ ++struct fsync ++{ ++ struct object obj; ++ unsigned int shm_idx; ++ enum fsync_type type; ++ struct list mutex_entry; ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ); ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type ); ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ); ++static void fsync_destroy( struct object *obj ); ++ ++const struct object_ops fsync_ops = ++{ ++ sizeof(struct fsync), /* size */ ++ &no_type, /* type */ ++ fsync_dump, /* dump */ ++ no_add_queue, /* add_queue */ ++ NULL, /* remove_queue */ ++ NULL, /* signaled */ ++ NULL, /* get_esync_fd */ ++ fsync_get_fsync_idx, /* get_fsync_idx */ ++ NULL, /* satisfied */ ++ no_signal, /* signal */ ++ no_get_fd, /* get_fd */ ++ fsync_map_access, /* map_access */ ++ default_get_sd, /* get_sd */ ++ default_set_sd, /* set_sd */ ++ no_get_full_name, /* get_full_name */ ++ no_lookup_name, /* lookup_name */ ++ directory_link_name, /* link_name */ ++ default_unlink_name, /* unlink_name */ ++ no_open_file, /* open_file */ ++ no_kernel_obj_list, /* get_kernel_obj_list */ ++ no_close_handle, /* close_handle */ ++ fsync_destroy /* destroy */ ++}; ++ ++static void fsync_dump( struct object *obj, int verbose ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ assert( obj->ops == &fsync_ops ); ++ fprintf( stderr, "fsync idx=%d\n", fsync->shm_idx ); ++} ++ ++static unsigned int fsync_get_fsync_idx( struct object *obj, enum fsync_type *type) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ *type = fsync->type; ++ return fsync->shm_idx; ++} ++ ++static unsigned int fsync_map_access( struct object *obj, unsigned int access ) ++{ ++ /* Sync objects have the same flags. */ ++ if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE; ++ if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE; ++ if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE; ++ if (access & GENERIC_ALL) access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE; ++ return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); ++} ++ ++static void fsync_destroy( struct object *obj ) ++{ ++ struct fsync *fsync = (struct fsync *)obj; ++ if (fsync->type == FSYNC_MUTEX) ++ list_remove( &fsync->mutex_entry ); ++} ++ ++static void *get_shm( unsigned int idx ) ++{ ++ int entry = (idx * 8) / pagesize; ++ int offset = (idx * 8) % pagesize; ++ ++ if (entry >= shm_addrs_size) ++ { ++ int new_size = max(shm_addrs_size * 2, entry + 1); ++ ++ if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) ))) ++ fprintf( stderr, "fsync: couldn't expand shm_addrs array to size %d\n", entry + 1 ); ++ ++ memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) ); ++ ++ shm_addrs_size = new_size; ++ } ++ ++ if (!shm_addrs[entry]) ++ { ++ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize ); ++ if (addr == (void *)-1) ++ { ++ fprintf( stderr, "fsync: failed to map page %d (offset %#lx): ", entry, entry * pagesize ); ++ perror( "mmap" ); ++ } ++ ++ if (debug_level) ++ fprintf( stderr, "fsync: Mapping page %d at %p.\n", entry, addr ); ++ ++ if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr )) ++ munmap( addr, pagesize ); /* someone beat us to it */ ++ } ++ ++ return (void *)((unsigned long)shm_addrs[entry] + offset); ++} ++ ++/* FIXME: This is rather inefficient... */ ++static unsigned int shm_idx_counter = 1; ++ ++unsigned int fsync_alloc_shm( int low, int high ) ++{ ++#ifdef __linux__ ++ int shm_idx; ++ int *shm; ++ ++ /* this is arguably a bit of a hack, but we need some way to prevent ++ * allocating shm for the master socket */ ++ if (!is_fsync_initialized) ++ return 0; ++ ++ shm_idx = shm_idx_counter++; ++ ++ while (shm_idx * 8 >= shm_size) ++ { ++ /* Better expand the shm section. */ ++ shm_size += pagesize; ++ if (ftruncate( shm_fd, shm_size ) == -1) ++ { ++ fprintf( stderr, "fsync: couldn't expand %s to size %jd: ", ++ shm_name, shm_size ); ++ perror( "ftruncate" ); ++ } ++ } ++ ++ shm = get_shm( shm_idx ); ++ assert(shm); ++ shm[0] = low; ++ shm[1] = high; ++ ++ return shm_idx; ++#else ++ return 0; ++#endif ++} ++ ++static int type_matches( enum fsync_type type1, enum fsync_type type2 ) ++{ ++ return (type1 == type2) || ++ ((type1 == FSYNC_AUTO_EVENT || type1 == FSYNC_MANUAL_EVENT) && ++ (type2 == FSYNC_AUTO_EVENT || type2 == FSYNC_MANUAL_EVENT)); ++} ++ ++struct fsync *create_fsync( struct object *root, const struct unicode_str *name, ++ unsigned int attr, int low, int high, enum fsync_type type, ++ const struct security_descriptor *sd ) ++{ ++#ifdef __linux__ ++ struct fsync *fsync; ++ ++ if ((fsync = create_named_object( root, &fsync_ops, name, attr, sd ))) ++ { ++ if (get_error() != STATUS_OBJECT_NAME_EXISTS) ++ { ++ /* initialize it if it didn't already exist */ ++ ++ /* Initialize the shared memory portion. We want to do this on the ++ * server side to avoid a potential though unlikely race whereby ++ * the same object is opened and used between the time it's created ++ * and the time its shared memory portion is initialized. */ ++ ++ fsync->shm_idx = fsync_alloc_shm( low, high ); ++ fsync->type = type; ++ if (type == FSYNC_MUTEX) ++ list_add_tail( &mutex_list, &fsync->mutex_entry ); ++ } ++ else ++ { ++ /* validate the type */ ++ if (!type_matches( type, fsync->type )) ++ { ++ release_object( &fsync->obj ); ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ return NULL; ++ } ++ } ++ } ++ ++ return fsync; ++#else ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++#endif ++} ++ ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} ++ ++/* shm layout for events or event-like objects. */ ++struct fsync_event ++{ ++ int signaled; ++ int unused; ++}; ++ ++void fsync_wake_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_wake_up( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_wake_up: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_wake_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_clear_futex( unsigned int shm_idx ) ++{ ++ struct fsync_event *event; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear_futex: index %u\n", shm_idx ); ++ ++ if (!shm_idx) ++ return; ++ ++ event = get_shm( shm_idx ); ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++void fsync_clear( struct object *obj ) ++{ ++ enum fsync_type type; ++ ++ if (debug_level) ++ fprintf( stderr, "fsync_clear: object %p\n", obj ); ++ ++ if (obj->ops->get_fsync_idx) ++ fsync_clear_futex( obj->ops->get_fsync_idx( obj, &type ) ); ++} ++ ++void fsync_set_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST )) ++ futex_wake( &event->signaled, INT_MAX ); ++} ++ ++void fsync_reset_event( struct fsync *fsync ) ++{ ++ struct fsync_event *event = get_shm( fsync->shm_idx ); ++ assert( fsync->obj.ops == &fsync_ops ); ++ ++ __atomic_store_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); ++} ++ ++struct mutex ++{ ++ int tid; ++ int count; /* recursion count */ ++}; ++ ++void fsync_abandon_mutexes( struct thread *thread ) ++{ ++ struct fsync *fsync; ++ ++ LIST_FOR_EACH_ENTRY( fsync, &mutex_list, struct fsync, mutex_entry ) ++ { ++ struct mutex *mutex = get_shm( fsync->shm_idx ); ++ ++ if (mutex->tid == thread->id) ++ { ++ if (debug_level) ++ fprintf( stderr, "fsync_abandon_mutexes() idx=%d\n", fsync->shm_idx ); ++ mutex->tid = ~0; ++ mutex->count = 0; ++ futex_wake( &mutex->tid, INT_MAX ); ++ } ++ } ++} ++ ++DECL_HANDLER(create_fsync) ++{ ++ struct fsync *fsync; ++ struct unicode_str name; ++ struct object *root; ++ const struct security_descriptor *sd; ++ const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); ++ ++ if (!do_fsync()) ++ { ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return; ++ } ++ ++ if (!objattr) return; ++ ++ if ((fsync = create_fsync( root, &name, objattr->attributes, req->low, ++ req->high, req->type, sd ))) ++ { ++ if (get_error() == STATUS_OBJECT_NAME_EXISTS) ++ reply->handle = alloc_handle( current->process, fsync, req->access, objattr->attributes ); ++ else ++ reply->handle = alloc_handle_no_access_check( current->process, fsync, ++ req->access, objattr->attributes ); ++ ++ reply->shm_idx = fsync->shm_idx; ++ reply->type = fsync->type; ++ release_object( fsync ); ++ } ++ ++ if (root) release_object( root ); ++} ++ ++DECL_HANDLER(open_fsync) ++{ ++ struct unicode_str name = get_req_unicode_str(); ++ ++ reply->handle = open_object( current->process, req->rootdir, req->access, ++ &fsync_ops, &name, req->attributes ); ++ ++ if (reply->handle) ++ { ++ struct fsync *fsync; ++ ++ if (!(fsync = (struct fsync *)get_handle_obj( current->process, reply->handle, ++ 0, &fsync_ops ))) ++ return; ++ ++ if (!type_matches( req->type, fsync->type )) ++ { ++ set_error( STATUS_OBJECT_TYPE_MISMATCH ); ++ release_object( fsync ); ++ return; ++ } ++ ++ reply->type = fsync->type; ++ reply->shm_idx = fsync->shm_idx; ++ release_object( fsync ); ++ } ++} ++ ++/* Retrieve the index of a shm section which will be signaled by the server. */ ++DECL_HANDLER(get_fsync_idx) ++{ ++ struct object *obj; ++ enum fsync_type type; ++ ++ if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL ))) ++ return; ++ ++ if (obj->ops->get_fsync_idx) ++ { ++ reply->shm_idx = obj->ops->get_fsync_idx( obj, &type ); ++ reply->type = type; ++ } ++ else ++ { ++ if (debug_level) ++ { ++ fprintf( stderr, "%04x: fsync: can't wait on object: ", current->id ); ++ obj->ops->dump( obj, 0 ); ++ } ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ } ++ ++ release_object( obj ); ++} ++ ++DECL_HANDLER(get_fsync_apc_idx) ++{ ++ reply->shm_idx = current->fsync_apc_idx; ++} +diff --git a/server/fsync.h b/server/fsync.h +new file mode 100644 +index 00000000000..a91939b7f0a +--- /dev/null ++++ b/server/fsync.h +@@ -0,0 +1,34 @@ ++/* ++ * futex-based synchronization objects ++ * ++ * Copyright (C) 2018 Zebediah Figura ++ * ++ * 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 ++ */ ++ ++extern int do_fsync(void); ++extern void fsync_init(void); ++extern unsigned int fsync_alloc_shm( int low, int high ); ++extern void fsync_wake_futex( unsigned int shm_idx ); ++extern void fsync_clear_futex( unsigned int shm_idx ); ++extern void fsync_wake_up( struct object *obj ); ++extern void fsync_clear( struct object *obj ); ++ ++struct fsync; ++ ++extern const struct object_ops fsync_ops; ++extern void fsync_set_event( struct fsync *fsync ); ++extern void fsync_reset_event( struct fsync *fsync ); ++extern void fsync_abandon_mutexes( struct thread *thread ); +diff --git a/server/handle.c b/server/handle.c +index cb5628b7e06..ff44446acc9 100644 +--- a/server/handle.c ++++ b/server/handle.c +@@ -124,6 +124,7 @@ static const struct object_ops handle_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/hook.c b/server/hook.c +index 61b5014c442..379f9c074d5 100644 +--- a/server/hook.c ++++ b/server/hook.c +@@ -82,6 +82,7 @@ static const struct object_ops hook_table_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/mailslot.c b/server/mailslot.c +index 18fef4b0466..b541524c9a4 100644 +--- a/server/mailslot.c ++++ b/server/mailslot.c +@@ -79,6 +79,7 @@ static const struct object_ops mailslot_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_get_fd, /* get_fd */ +@@ -138,6 +139,7 @@ static const struct object_ops mail_writer_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mail_writer_get_fd, /* get_fd */ +@@ -202,6 +204,7 @@ static const struct object_ops mailslot_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -233,6 +236,7 @@ static const struct object_ops mailslot_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + mailslot_device_file_get_fd, /* get_fd */ +diff --git a/server/main.c b/server/main.c +index 3e02cbb3832..7a09436b637 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -37,6 +37,7 @@ + #include "thread.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + /* command-line options */ + int debug_level = 0; +@@ -141,8 +142,14 @@ int main( int argc, char *argv[] ) + sock_init(); + open_master_socket(); + ++ if (do_fsync()) ++ fsync_init(); ++ + if (do_esync()) + esync_init(); ++ ++ if (!do_fsync() && !do_esync()) ++ fprintf( stderr, "wineserver: using server-side synchronization.\n" ); + + if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); + set_current_time(); +diff --git a/server/mapping.c b/server/mapping.c +index 10def3ca694..d55df5145f7 100644 +--- a/server/mapping.c ++++ b/server/mapping.c +@@ -69,6 +69,7 @@ static const struct object_ops ranges_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -106,6 +107,7 @@ static const struct object_ops shared_map_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -165,6 +167,7 @@ static const struct object_ops mapping_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + mapping_get_fd, /* get_fd */ +diff --git a/server/mutex.c b/server/mutex.c +index 1235ab4731f..ccdc383f0df 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -62,6 +62,7 @@ static const struct object_ops mutex_ops = + remove_queue, /* remove_queue */ + mutex_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + mutex_satisfied, /* satisfied */ + mutex_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index e59a5b6c183..d0ea32a24d5 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -119,6 +119,7 @@ static const struct object_ops named_pipe_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -164,6 +165,7 @@ static const struct object_ops pipe_server_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -208,6 +210,7 @@ static const struct object_ops pipe_client_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + default_fd_get_esync_fd, /* get_esync_fd */ ++ default_fd_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + pipe_end_get_fd, /* get_fd */ +@@ -256,6 +259,7 @@ static const struct object_ops named_pipe_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -288,6 +292,7 @@ static const struct object_ops named_pipe_device_file_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_device_file_get_fd, /* get_fd */ +diff --git a/server/object.h b/server/object.h +index 5b6bb9cbfe1..4c92b174ef3 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -70,6 +70,8 @@ struct object_ops + int (*signaled)(struct object *,struct wait_queue_entry *); + /* return the esync fd for this object */ + int (*get_esync_fd)(struct object *, enum esync_type *type); ++ /* return the fsync shm idx for this object */ ++ unsigned int (*get_fsync_idx)(struct object *, enum fsync_type *type); + /* wait satisfied */ + void (*satisfied)(struct object *,struct wait_queue_entry *); + /* signal an object */ +diff --git a/server/process.c b/server/process.c +index df50955f621..c4f51dcad30 100644 +--- a/server/process.c ++++ b/server/process.c +@@ -50,6 +50,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + /* process object */ + +@@ -70,6 +71,7 @@ static void process_poll_event( struct fd *fd, int event ); + static struct list *process_get_kernel_obj_list( struct object *obj ); + static void process_destroy( struct object *obj ); + static int process_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void terminate_process( struct process *process, struct thread *skip, int exit_code ); + + static const struct object_ops process_ops = +@@ -81,6 +83,7 @@ static const struct object_ops process_ops = + remove_queue, /* remove_queue */ + process_signaled, /* signaled */ + process_get_esync_fd, /* get_esync_fd */ ++ process_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -133,6 +136,7 @@ static const struct object_ops startup_info_ops = + remove_queue, /* remove_queue */ + startup_info_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -180,6 +184,7 @@ static const struct object_ops job_ops = + remove_queue, /* remove_queue */ + job_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -547,6 +552,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + process->rawinput_mouse = NULL; + process->rawinput_kbd = NULL; + process->esync_fd = -1; ++ process->fsync_idx = 0; + list_init( &process->kernel_object ); + list_init( &process->thread_list ); + list_init( &process->locks ); +@@ -603,6 +609,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, + if (!token_assign_label( process->token, security_high_label_sid )) + goto error; + ++ if (do_fsync()) ++ process->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + process->esync_fd = esync_create_fd( 0, 0 ); + +@@ -685,6 +694,13 @@ static int process_get_esync_fd( struct object *obj, enum esync_type *type ) + return process->esync_fd; + } + ++static unsigned int process_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct process *process = (struct process *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return process->fsync_idx; ++} ++ + static unsigned int process_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +diff --git a/server/process.h b/server/process.h +index eec69ddbcaf..abb4ed31b48 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -99,6 +99,7 @@ struct process + const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */ + struct list kernel_object; /* list of kernel object pointers */ + int esync_fd; /* esync file descriptor (signaled on exit) */ ++ unsigned int fsync_idx; + }; + + #define CPU_FLAG(cpu) (1 << (cpu)) +diff --git a/server/protocol.def b/server/protocol.def +index 2a8662354f6..381bd8d8218 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3766,3 +3766,57 @@ enum esync_type + /* Retrieve the fd to wait on for user APCs. */ + @REQ(get_esync_apc_fd) + @END ++ ++enum fsync_type ++{ ++ FSYNC_SEMAPHORE = 1, ++ FSYNC_AUTO_EVENT, ++ FSYNC_MANUAL_EVENT, ++ FSYNC_MUTEX, ++ FSYNC_AUTO_SERVER, ++ FSYNC_MANUAL_SERVER, ++ FSYNC_QUEUE, ++}; ++ ++/* Create a new futex-based synchronization object */ ++@REQ(create_fsync) ++ unsigned int access; /* wanted access rights */ ++ int low; /* initial value of low word */ ++ int high; /* initial value of high word */ ++ int type; /* type of fsync object */ ++ VARARG(objattr,object_attributes); /* object attributes */ ++@REPLY ++ obj_handle_t handle; /* handle to the object */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Open an fsync object */ ++@REQ(open_fsync) ++ unsigned int access; /* wanted access rights */ ++ unsigned int attributes; /* object attributes */ ++ obj_handle_t rootdir; /* root directory */ ++ int type; /* type of fsync object */ ++ VARARG(name,unicode_str); /* object name */ ++@REPLY ++ obj_handle_t handle; /* handle to the event */ ++ int type; /* type of fsync object */ ++ unsigned int shm_idx; /* this object's index into the shm section */ ++@END ++ ++/* Retrieve the shm index for an object. */ ++@REQ(get_fsync_idx) ++ obj_handle_t handle; /* handle to the object */ ++@REPLY ++ int type; ++ unsigned int shm_idx; ++@END ++ ++@REQ(fsync_msgwait) ++ int in_msgwait; /* are we in a message wait? */ ++@END ++ ++@REQ(get_fsync_apc_idx) ++@REPLY ++ unsigned int shm_idx; ++@END +diff --git a/server/queue.c b/server/queue.c +index a78748b96ca..dccf43197cf 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -44,6 +44,7 @@ + #include "request.h" + #include "user.h" + #include "esync.h" ++#include "fsync.h" + + #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE + #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST)) +@@ -147,6 +148,8 @@ struct msg_queue + unsigned int ignore_post_msg; /* ignore post messages newer than this unique id */ + int esync_fd; /* esync file descriptor (signalled on message) */ + int esync_in_msgwait; /* our thread is currently waiting on us */ ++ unsigned int fsync_idx; ++ int fsync_in_msgwait; /* our thread is currently waiting on us */ + }; + + struct hotkey +@@ -164,6 +167,7 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent + static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void msg_queue_destroy( struct object *obj ); + static void msg_queue_poll_event( struct fd *fd, int event ); +@@ -180,6 +184,7 @@ static const struct object_ops msg_queue_ops = + msg_queue_remove_queue, /* remove_queue */ + msg_queue_signaled, /* signaled */ + msg_queue_get_esync_fd, /* get_esync_fd */ ++ msg_queue_get_fsync_idx, /* get_fsync_idx */ + msg_queue_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -218,6 +223,7 @@ static const struct object_ops thread_input_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -321,12 +327,17 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ + queue->ignore_post_msg = 0; + queue->esync_fd = -1; + queue->esync_in_msgwait = 0; ++ queue->fsync_idx = 0; ++ queue->fsync_in_msgwait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); + list_init( &queue->expired_timers ); + for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] ); + ++ if (do_fsync()) ++ queue->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + queue->esync_fd = esync_create_fd( 0, 0 ); + +@@ -509,6 +520,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits + queue->keystate_lock = 0; + } + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -970,6 +984,9 @@ static int is_queue_hung( struct msg_queue *queue ) + return 0; /* thread is waiting on queue -> not hung */ + } + ++ if (do_fsync() && queue->fsync_in_msgwait) ++ return 0; /* thread is waiting on queue in absentia -> not hung */ ++ + if (do_esync() && queue->esync_in_msgwait) + return 0; /* thread is waiting on queue in absentia -> not hung */ + +@@ -1035,6 +1052,13 @@ static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type ) + return queue->esync_fd; + } + ++static unsigned int msg_queue_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct msg_queue *queue = (struct msg_queue *)obj; ++ *type = FSYNC_QUEUE; ++ return queue->fsync_idx; ++} ++ + static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct msg_queue *queue = (struct msg_queue *)obj; +@@ -2517,6 +2541,9 @@ DECL_HANDLER(get_queue_status) + reply->changed_bits = queue->changed_bits; + queue->changed_bits &= ~req->clear_bits; + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + } +@@ -3536,3 +3563,18 @@ DECL_HANDLER(esync_msgwait) + if (queue->fd) + set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); + } ++ ++DECL_HANDLER(fsync_msgwait) ++{ ++ struct msg_queue *queue = get_current_queue(); ++ ++ if (!queue) return; ++ queue->fsync_in_msgwait = req->in_msgwait; ++ ++ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT)) ++ set_event( current->process->idle_event ); ++ ++ /* and start/stop waiting on the driver */ ++ if (queue->fd) ++ set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 ); ++} +diff --git a/server/registry.c b/server/registry.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -168,6 +168,7 @@ static const struct object_ops key_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/request.c b/server/request.c +index 20b0ec309f3..9fd08139375 100644 +--- a/server/request.c ++++ b/server/request.c +@@ -97,6 +97,7 @@ static const struct object_ops master_socket_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/semaphore.c b/server/semaphore.c +index d7d3a24e48f..604721d0d5f 100644 +--- a/server/semaphore.c ++++ b/server/semaphore.c +@@ -59,6 +59,7 @@ static const struct object_ops semaphore_ops = + remove_queue, /* remove_queue */ + semaphore_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + semaphore_satisfied, /* satisfied */ + semaphore_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/serial.c b/server/serial.c +index a50ace9903f..78d33460892 100644 +--- a/server/serial.c ++++ b/server/serial.c +@@ -93,6 +93,7 @@ static const struct object_ops serial_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + serial_get_fd, /* get_fd */ +diff --git a/server/signal.c b/server/signal.c +index b6d6dcfc4b6..f5ac61b6975 100644 +--- a/server/signal.c ++++ b/server/signal.c +@@ -68,6 +68,7 @@ static const struct object_ops handler_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/sock.c b/server/sock.c +index c2dfa8fb8ce..080efc98942 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -173,6 +173,7 @@ static const struct object_ops sock_ops = + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + sock_get_fd, /* get_fd */ +@@ -1175,6 +1176,7 @@ static const struct object_ops ifchange_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + ifchange_get_fd, /* get_fd */ +@@ -1396,6 +1398,7 @@ static const struct object_ops socket_device_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/symlink.c b/server/symlink.c +index 07f3c924f25..0e3e9ee9864 100644 +--- a/server/symlink.c ++++ b/server/symlink.c +@@ -61,6 +61,7 @@ static const struct object_ops symlink_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/thread.c b/server/thread.c +index 1a245c58396..1fb3603f2a7 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -52,6 +52,7 @@ + #include "user.h" + #include "security.h" + #include "esync.h" ++#include "fsync.h" + + + #ifdef __i386__ +@@ -112,6 +113,7 @@ static const struct object_ops thread_apc_ops = + remove_queue, /* remove_queue */ + thread_apc_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -150,6 +152,7 @@ static const struct object_ops context_ops = + remove_queue, /* remove_queue */ + context_signaled, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -173,6 +176,7 @@ + static void dump_thread( struct object *obj, int verbose ); + static int thread_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int thread_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static unsigned int thread_map_access( struct object *obj, unsigned int access ); + static void thread_poll_event( struct fd *fd, int event ); + static struct list *thread_get_kernel_obj_list( struct object *obj ); +@@ -187,6 +191,7 @@ static const struct object_ops thread_ops = + remove_queue, /* remove_queue */ + thread_signaled, /* signaled */ + thread_get_esync_fd, /* get_esync_fd */ ++ thread_get_fsync_idx, /* get_fsync_idx */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -228,6 +233,7 @@ static inline void init_thread_structure( struct thread *thread ) + thread->entry_point = 0; + thread->esync_fd = -1; + thread->esync_apc_fd = -1; ++ thread->fsync_idx = 0; + thread->system_regs = 0; + thread->queue = NULL; + thread->wait = NULL; +@@ -364,6 +370,12 @@ struct thread *create_thread( int fd, struct process *process, const struct secu + return NULL; + } + ++ if (do_fsync()) ++ { ++ thread->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ thread->fsync_apc_idx = fsync_alloc_shm( 0, 0 ); ++ } ++ + if (do_esync()) + { + thread->esync_fd = esync_create_fd( 0, 0 ); +@@ -484,6 +496,13 @@ static int thread_get_esync_fd( struct object *obj, enum esync_type *type ) + return thread->esync_fd; + } + ++static unsigned int thread_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct thread *thread = (struct thread *)obj; ++ *type = FSYNC_MANUAL_SERVER; ++ return thread->fsync_idx; ++} ++ + static unsigned int thread_map_access( struct object *obj, unsigned int access ) + { + access = default_map_access( obj, access ); +@@ -533,6 +552,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca + apc->result.type = APC_NONE; + if (owner) grab_object( owner ); + } ++ + return apc; + } + +@@ -1068,6 +1088,9 @@ void wake_up( struct object *obj, int max ) + struct list *ptr; + int ret; + ++ if (do_fsync()) ++ fsync_wake_up( obj ); ++ + if (do_esync()) + esync_wake_up( obj ); + +@@ -1158,6 +1181,9 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr + { + wake_thread( thread ); + ++ if (do_fsync() && queue == &thread->user_apc) ++ fsync_wake_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && queue == &thread->user_apc) + esync_wake_fd( thread->esync_apc_fd ); + } +@@ -1208,6 +1234,9 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system + list_remove( ptr ); + } + ++ if (do_fsync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) ++ fsync_clear_futex( thread->fsync_apc_idx ); ++ + if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc )) + esync_clear( thread->esync_apc_fd ); + +@@ -1327,6 +1356,8 @@ void kill_thread( struct thread *thread, int violent_death ) + } + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); ++ if (do_fsync()) ++ fsync_abandon_mutexes( thread ); + if (do_esync()) + esync_abandon_mutexes( thread ); + if (violent_death) +diff --git a/server/thread.h b/server/thread.h +index 0f6108b684a..53627631343 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -56,6 +56,8 @@ struct thread + struct list mutex_list; /* list of currently owned mutexes */ + int esync_fd; /* esync file descriptor (signalled on exit) */ + int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */ ++ unsigned int fsync_idx; ++ unsigned int fsync_apc_idx; + unsigned int system_regs; /* which system regs have been set */ + struct msg_queue *queue; /* message queue */ + struct thread_wait *wait; /* current wait condition if sleeping */ +diff --git a/server/timer.c b/server/timer.c +index dcbc9e2ece5..9d9d7b8b40c 100644 +--- a/server/timer.c ++++ b/server/timer.c +@@ -37,6 +37,7 @@ + #include "handle.h" + #include "request.h" + #include "esync.h" ++#include "fsync.h" + + static const WCHAR timer_name[] = {'T','i','m','e','r'}; + +@@ -50,11 +51,13 @@ struct timer + client_ptr_t callback; /* callback APC function */ + client_ptr_t arg; /* callback argument */ + int esync_fd; /* esync file descriptor */ ++ unsigned int fsync_idx; /* fsync shm index */ + }; + + static void timer_dump( struct object *obj, int verbose ); + static int timer_signaled( struct object *obj, struct wait_queue_entry *entry ); + static int timer_get_esync_fd( struct object *obj, enum esync_type *type ); ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ); + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ); + static void timer_destroy( struct object *obj ); + +@@ -69,6 +72,7 @@ static const struct object_ops timer_ops = + remove_queue, /* remove_queue */ + timer_signaled, /* signaled */ + timer_get_esync_fd, /* get_esync_fd */ ++ timer_get_fsync_idx, /* get_fsync_idx */ + timer_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -105,6 +109,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str + timer->thread = NULL; + timer->esync_fd = -1; + ++ if (do_fsync()) ++ timer->fsync_idx = fsync_alloc_shm( 0, 0 ); ++ + if (do_esync()) + timer->esync_fd = esync_create_fd( 0, 0 ); + } +@@ -181,6 +188,9 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period + period = 0; /* period doesn't make any sense for a manual timer */ + timer->signaled = 0; + ++ if (do_fsync()) ++ fsync_clear( &timer->obj ); ++ + if (do_esync()) + esync_clear( timer->esync_fd ); + } +@@ -223,6 +233,13 @@ static int timer_get_esync_fd( struct object *obj, enum esync_type *type ) + return timer->esync_fd; + } + ++static unsigned int timer_get_fsync_idx( struct object *obj, enum fsync_type *type ) ++{ ++ struct timer *timer = (struct timer *)obj; ++ *type = timer->manual ? FSYNC_MANUAL_SERVER : FSYNC_AUTO_SERVER; ++ return timer->fsync_idx; ++} ++ + static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry ) + { + struct timer *timer = (struct timer *)obj; +diff --git a/server/token.c b/server/token.c +index 0f128728b0f..5a80b6fd50e 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -148,6 +148,7 @@ static const struct object_ops token_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/winstation.c b/server/winstation.c +index 1a031248a7c..388b116bc67 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -65,6 +65,7 @@ static const struct object_ops winstation_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +@@ -91,6 +92,7 @@ static const struct object_ops desktop_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +diff --git a/server/window.c b/server/window.c +index 996bff5ef6d..50b600b4853 100644 +--- a/server/window.c ++++ b/server/window.c +@@ -113,6 +113,7 @@ static const struct object_ops window_ops = + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* get_esync_fd */ ++ NULL, /* get_fsync_idx */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ +From f072cf6366aa8385efdcb4a7b4c49e122f00b750 Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Mon, 14 Feb 2022 12:51:27 -0500 +Subject: [PATCH] fsync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/fsync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index 39d969f061d..d468782667a 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -576,6 +576,9 @@ NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != FSYNC_MANUAL_EVENT && obj->type != FSYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) + futex_wake( &event->signaled, INT_MAX ); + +From 69afcb164ccf8d3ecd5e94cf79c1e31698e14e5c Mon Sep 17 00:00:00 2001 +From: Derek Lesho +Date: Wed, 2 Feb 2022 17:02:44 -0500 +Subject: [PATCH] esync: Type-check HANDLE in esync_set_event. + +Signed-off-by: Derek Lesho +--- + dlls/ntdll/unix/esync.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 55c5695964d..4663374653a 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -526,6 +526,9 @@ NTSTATUS esync_set_event( HANDLE handle ) + if ((ret = get_object( handle, &obj ))) return ret; + event = obj->shm; + ++ if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) ++ return STATUS_OBJECT_TYPE_MISMATCH; ++ + if (obj->type == ESYNC_MANUAL_EVENT) + { + /* Acquire the spinlock. */ +diff --git a/server/queue.c b/server/queue.c +index 012795e410d..ba442324276 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -2536,6 +2536,8 @@ DECL_HANDLER(set_queue_mask) + if (req->skip_wait) queue->wake_mask = queue->changed_mask = 0; + else wake_up( &queue->obj, 0 ); + } ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); + + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); +@@ -2807,6 +2809,9 @@ DECL_HANDLER(get_message) + queue->changed_mask = req->changed_mask; + set_error( STATUS_PENDING ); /* FIXME */ + ++ if (do_fsync() && !is_signaled( queue )) ++ fsync_clear( &queue->obj ); ++ + if (do_esync() && !is_signaled( queue )) + esync_clear( queue->esync_fd ); + + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-59485f0.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-59485f0.patch new file mode 100644 index 000000000..bf2406682 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-59485f0.patch @@ -0,0 +1,917 @@ +From 04904bbab87d5662e40743f26534a74ee7d2c943 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 16 Mar 2022 11:50:28 +0100 +Subject: Import futex_waitv support from Proton Experimental + https://github.com/ValveSoftware/wine/tree/205874085341305cac84ae61a0141d2fa5b892dd + From ntdll/fsync: Support futex_waitv() API to ntdll: Include linux/futex.h in fsync.c + + +diff --git a/configure b/configure +index 7ea8b1c0ceb..03533857085 100755 +--- a/configure ++++ b/configure +@@ -7982,6 +7982,12 @@ if test "x$ac_cv_header_linux_filter_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/futex.h" "ac_cv_header_linux_futex_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_futex_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_FUTEX_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "linux/hdreg.h" "ac_cv_header_linux_hdreg_h" "$ac_includes_default" + if test "x$ac_cv_header_linux_hdreg_h" = xyes +diff --git a/configure.ac b/configure.ac +index 815e40fa08d..0082bd7e4a7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -441,6 +441,7 @@ AC_CHECK_HEADERS(\ + link.h \ + linux/cdrom.h \ + linux/filter.h \ ++ linux/futex.h \ + linux/hdreg.h \ + linux/hidraw.h \ + linux/input.h \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..0c6c5b8553b 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -953,6 +953,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); + if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); +@@ -968,6 +970,12 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ { ++ usleep( 0 ); ++ if (!event->signaled) ++ break; ++ } + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } +@@ -996,6 +1004,9 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + while (1) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + ret = do_poll( fds, pollcount, timeout ? &end : NULL ); + if (ret > 0) + { +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index d0f8be6c309..f9f6e252b6c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -5363,6 +5363,230 @@ static NTSTATUS set_pending_write( HANDLE device ) + return status; + } + ++static pthread_mutex_t async_file_read_mutex = PTHREAD_MUTEX_INITIALIZER; ++static pthread_cond_t async_file_read_cond = PTHREAD_COND_INITIALIZER; ++ ++struct async_file_read_job ++{ ++ HANDLE handle; ++ int unix_handle; ++ int needs_close; ++ HANDLE event; ++ IO_STATUS_BLOCK *io; ++ void *buffer; ++ ULONG length; ++ LARGE_INTEGER offset; ++ DWORD thread_id; ++ LONG cancelled; ++ struct list queue_entry; ++ struct async_file_read_job *next; ++}; ++ ++ ++static struct list async_file_read_queue = LIST_INIT( async_file_read_queue ); ++static struct async_file_read_job *async_file_read_running, *async_file_read_free; ++ ++static void async_file_complete_io( struct async_file_read_job *job, NTSTATUS status, ULONG total ) ++{ ++ job->io->Status = status; ++ job->io->Information = total; ++ ++ if (job->event) NtSetEvent( job->event, NULL ); ++} ++ ++static void *async_file_read_thread(void *dummy) ++{ ++ struct async_file_read_job *job, *ptr; ++ ULONG buffer_length = 0; ++ void *buffer = NULL; ++ struct list *entry; ++ NTSTATUS status; ++ ULONG total; ++ int result; ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ while (1) ++ { ++ while (!(entry = list_head( &async_file_read_queue ))) ++ { ++ pthread_cond_wait( &async_file_read_cond, &async_file_read_mutex ); ++ continue; ++ } ++ ++ job = LIST_ENTRY( entry, struct async_file_read_job, queue_entry ); ++ list_remove( entry ); ++ ++ total = 0; ++ ++ if ( job->cancelled ) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ status = STATUS_CANCELLED; ++ goto done; ++ } ++ ++ job->next = async_file_read_running; ++ async_file_read_running = job; ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ if (!buffer_length) ++ { ++ buffer = malloc(job->length); ++ buffer_length = job->length; ++ } ++ else if (buffer_length < job->length) ++ { ++ buffer = realloc(buffer, job->length); ++ buffer_length = job->length; ++ } ++ ++ while ((result = pread( job->unix_handle, buffer, job->length, job->offset.QuadPart )) == -1) ++ { ++ if (errno != EINTR) ++ { ++ status = errno_to_status( errno ); ++ goto done; ++ } ++ if (job->cancelled) ++ break; ++ } ++ ++ total = result; ++ status = (total || !job->length) ? STATUS_SUCCESS : STATUS_END_OF_FILE; ++done: ++ if (job->needs_close) close( job->unix_handle ); ++ ++ if (!InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ if (status == STATUS_SUCCESS) ++ memcpy( job->buffer, buffer, total ); ++ ++ async_file_complete_io( job, status, total ); ++ } ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (status != STATUS_CANCELLED) ++ { ++ ptr = async_file_read_running; ++ if (job == ptr) ++ { ++ async_file_read_running = job->next; ++ } ++ else ++ { ++ while (ptr && ptr->next != job) ++ ptr = ptr->next; ++ ++ assert( ptr ); ++ ptr->next = job->next; ++ } ++ } ++ ++ job->next = async_file_read_free; ++ async_file_read_free = job; ++ } ++ ++ return NULL; ++} ++ ++static pthread_once_t async_file_read_once = PTHREAD_ONCE_INIT; ++ ++static void async_file_read_init(void) ++{ ++ pthread_t async_file_read_thread_id; ++ pthread_attr_t pthread_attr; ++ ++ ERR("HACK: AC Odyssey async read workaround.\n"); ++ ++ pthread_attr_init( &pthread_attr ); ++ pthread_attr_setscope( &pthread_attr, PTHREAD_SCOPE_SYSTEM ); ++ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED ); ++ ++ pthread_create( &async_file_read_thread_id, &pthread_attr, (void * (*)(void *))async_file_read_thread, NULL); ++ pthread_attr_destroy( &pthread_attr ); ++} ++ ++static NTSTATUS queue_async_file_read( HANDLE handle, int unix_handle, int needs_close, HANDLE event, ++ IO_STATUS_BLOCK *io, void *buffer, ULONG length, LARGE_INTEGER *offset ) ++{ ++ struct async_file_read_job *job; ++ ++ pthread_once( &async_file_read_once, async_file_read_init ); ++ ++ NtResetEvent( event, NULL ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (async_file_read_free) ++ { ++ job = async_file_read_free; ++ async_file_read_free = async_file_read_free->next; ++ } ++ else ++ { ++ if (!(job = malloc( sizeof(*job) ))) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return STATUS_NO_MEMORY; ++ } ++ } ++ ++ job->handle = handle; ++ job->unix_handle = unix_handle; ++ job->needs_close = needs_close; ++ job->event = event; ++ job->io = io; ++ job->buffer = buffer; ++ job->length = length; ++ job->offset = *offset; ++ job->thread_id = GetCurrentThreadId(); ++ job->cancelled = 0; ++ ++ list_add_tail( &async_file_read_queue, &job->queue_entry ); ++ ++ pthread_cond_signal( &async_file_read_cond ); ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS cancel_async_file_read( HANDLE handle, IO_STATUS_BLOCK *io ) ++{ ++ DWORD thread_id = GetCurrentThreadId(); ++ struct async_file_read_job *job; ++ unsigned int count = 0; ++ ++ TRACE( "handle %p, io %p.\n", handle, io ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ job = async_file_read_running; ++ while (job) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ job = job->next; ++ } ++ ++ LIST_FOR_EACH_ENTRY( job, &async_file_read_queue, struct async_file_read_job, queue_entry ) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ } ++ ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return count ? STATUS_SUCCESS : STATUS_NOT_FOUND; ++} + + /****************************************************************************** + * NtReadFile (NTDLL.@) +@@ -5404,6 +5628,13 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo + goto done; + } + ++ if (ac_odyssey && async_read && length && event && !apc) ++ { ++ status = queue_async_file_read( handle, unix_handle, needs_close, event, io, buffer, length, offset ); ++ needs_close = 0; ++ goto err; ++ } ++ + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + { + /* async I/O doesn't make sense on regular files */ +@@ -6823,6 +7054,9 @@ NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status ) + + TRACE( "%p %p\n", handle, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, NULL )) ++ return (io_status->Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -6848,6 +7082,9 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_ + + TRACE( "%p %p %p\n", handle, io, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, io )) ++ return (io_status->Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index f5ae0a821b0..284e2fcce01 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -27,6 +27,9 @@ + #include + #include + #include ++#ifdef HAVE_LINUX_FUTEX_H ++# include ++#endif + #include + #include + #include +@@ -39,6 +42,7 @@ + # include + #endif + #include ++#include + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -54,43 +58,82 @@ + WINE_DEFAULT_DEBUG_CHANNEL(fsync); + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; ++#include "poppack.h" ++ ++/* futex_waitv interface */ ++ ++#ifndef __NR_futex_waitv ++# define __NR_futex_waitv 449 + #endif +- int val; +- int bitset; ++ ++#ifndef FUTEX_32 ++# define FUTEX_32 2 ++struct futex_waitv { ++ uint64_t val; ++ uint64_t uaddr; ++ uint32_t flags; ++ uint32_t __reserved; + }; +-#include "poppack.h" ++#endif ++ ++#define u64_to_ptr(x) (void *)(uintptr_t)(x) + +-static inline void small_pause(void) ++struct timespec64 + { +-#if defined(__i386__) || defined(__x86_64__) +- __asm__ __volatile__( "rep;nop" : : : "memory" ); +-#else +- __asm__ __volatile__( "" : : : "memory" ); +-#endif ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; + } + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) ++static inline void futex_vector_set( struct futex_waitv *waitv, int *addr, int val ) + { +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++ waitv->uaddr = (uintptr_t) addr; ++ waitv->val = val; ++ waitv->flags = FUTEX_32; ++ waitv->__reserved = 0; + } + +-static inline int futex_wake( int *addr, int val ) ++static void simulate_sched_quantum(void) + { +- return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++ if (!fsync_simulate_sched_quantum) return; ++ /* futex wait is often very quick to resume a waiting thread when woken. ++ * That reveals synchonization bugs in some games which happen to work on ++ * Windows due to the waiting threads having some minimal delay to wake up. */ ++ usleep(0); + } + +-static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++static inline int futex_wait_multiple( const struct futex_waitv *futexes, ++ int count, const ULONGLONG *end ) + { +- return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++ if (end) ++ { ++ struct timespec64 timeout; ++ ULONGLONG tmp = *end - SECS_1601_TO_1970 * TICKSPERSEC; ++ timeout.tv_sec = tmp / (ULONGLONG)TICKSPERSEC; ++ timeout.tv_nsec = (tmp % TICKSPERSEC) * 100; ++ ++ return syscall( __NR_futex_waitv, futexes, count, 0, &timeout, CLOCK_REALTIME ); ++ } ++ else ++ { ++ return syscall( __NR_futex_waitv, futexes, count, 0, NULL, 0 ); ++ } + } + +-static unsigned int spincount = 100; ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} + + int do_fsync(void) + { +@@ -99,11 +142,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; +- if (getenv("WINEFSYNC_SPINCOUNT")) +- spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); + } + + return do_fsync_cached; +@@ -646,64 +694,30 @@ NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) + return STATUS_SUCCESS; + } + +-static LONGLONG update_timeout( ULONGLONG end ) +-{ +- LARGE_INTEGER now; +- LONGLONG timeleft; +- +- NtQuerySystemTime( &now ); +- timeleft = end - now.QuadPart; +- if (timeleft < 0) timeleft = 0; +- return timeleft; +-} +- + static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) + { ++ struct futex_waitv futexes[2]; + int ret; + ++ futex_vector_set( &futexes[0], addr, val ); ++ + if (alertable) + { + int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; +- struct futex_wait_block futexes[2]; + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + +- futexes[0].addr = addr; +- futexes[0].val = val; +- futexes[1].addr = apc_futex; +- futexes[1].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[0].pad = futexes[1].pad = 0; +-#endif +- futexes[0].bitset = futexes[1].bitset = ~0; ++ futex_vector_set( &futexes[1], apc_futex, 0 ); + +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait_multiple( futexes, 2, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, 2, NULL ); ++ ret = futex_wait_multiple( futexes, 2, end ); + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + } + else + { +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait( addr, val, &tmo_p ); +- } +- else +- ret = futex_wait( addr, val, NULL ); ++ ret = futex_wait_multiple( futexes, 1, end ); + } + + if (!ret) +@@ -719,12 +733,11 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + static const LARGE_INTEGER zero = {0}; + +- struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct futex_waitv futexes[MAXIMUM_WAIT_OBJECTS + 1]; + struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ BOOL msgwait = FALSE, waited = FALSE; + int has_fsync = 0, has_server = 0; +- BOOL msgwait = FALSE; + int dummy_futex = 0; +- unsigned int spin; + LONGLONG timeleft; + LARGE_INTEGER now; + DWORD waitcount; +@@ -834,23 +847,15 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + struct semaphore *semaphore = obj->shm; + int current; + +- /* It would be a little clearer (and less error-prone) +- * to use a dedicated interlocked_dec_if_nonzero() +- * helper, but nesting loops like that is probably not +- * great for performance... */ +- for (spin = 0; spin <= spincount || current; ++spin) ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) + { +- if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) +- && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &semaphore->count; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &semaphore->count, 0 ); + break; + } + case FSYNC_MUTEX: +@@ -862,28 +867,25 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + mutex->count++; ++ if (waited) simulate_sched_quantum(); + return i; + } + +- for (spin = 0; spin <= spincount; ++spin) ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) + { +- if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return i; +- } +- else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) +- { +- TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return STATUS_ABANDONED_WAIT_0 + i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ if (waited) simulate_sched_quantum(); ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; + } + +- futexes[i].addr = &mutex->tid; +- futexes[i].val = tid; ++ futex_vector_set( &futexes[i], &mutex->tid, tid ); + break; + } + case FSYNC_AUTO_EVENT: +@@ -891,18 +893,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) + { +- if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + case FSYNC_MANUAL_EVENT: +@@ -911,18 +912,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + { +- if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + default: +@@ -933,31 +933,22 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + else + { + /* Avoid breaking things entirely. */ +- futexes[i].addr = &dummy_futex; +- futexes[i].val = dummy_futex; ++ futex_vector_set( &futexes[i], &dummy_futex, dummy_futex ); + } +- +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; + } + + if (alertable) + { + /* We already checked if it was signaled; don't bother doing it again. */ +- futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; +- futexes[i].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; +- i++; ++ futex_vector_set( &futexes[i++], ntdll_get_thread_data()->fsync_apc_futex, 0 ); + } + waitcount = i; + + /* Looks like everything is contended, so wait. */ + ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + if (timeout && !timeout->QuadPart) + { + /* Unlike esync, we already know that we've timed out, so we +@@ -965,17 +956,8 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } +- else if (timeout) +- { +- LONGLONG timeleft = update_timeout( end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + +- ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ret = futex_wait_multiple( futexes, waitcount, timeout ? &end : NULL ); + + /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, + * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to +@@ -986,6 +968,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } ++ else waited = TRUE; + } /* while (1) */ + } + else +@@ -1090,6 +1073,7 @@ tryagain: + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +@@ -1109,7 +1093,10 @@ tryagain: + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; +- if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ int current; ++ ++ if (!(current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ || __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) != current) + goto tooslow; + break; + } +@@ -1151,6 +1138,7 @@ tooslow: + for (--i; i >= 0; i--) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 0360bc64b00..f30d900a07b 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2028,6 +2028,34 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = + }; + + #endif /* _WIN64 */ ++ ++BOOL ac_odyssey; ++BOOL fsync_simulate_sched_quantum; ++ ++static void hacks_init(void) ++{ ++ static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe"; ++ static const char ac_odyssey_exe[] = "ACOdyssey.exe"; ++ const char *env_str; ++ ++ if (main_argc > 1 && strstr(main_argv[1], ac_odyssey_exe)) ++ { ++ ERR("HACK: AC Odyssey sync tweak on.\n"); ++ ac_odyssey = TRUE; ++ return; ++ } ++ env_str = getenv("WINE_FSYNC_SIMULATE_SCHED_QUANTUM"); ++ if (env_str) ++ fsync_simulate_sched_quantum = !!atoi(env_str); ++ else if (main_argc > 1) ++ fsync_simulate_sched_quantum = !!strstr(main_argv[1], upc_exe); ++ if (fsync_simulate_sched_quantum) ++ ERR("HACK: Simulating sched quantum in fsync.\n"); ++ ++ env_str = getenv("SteamGameId"); ++ if (env_str && !strcmp(env_str, "50130")) ++ setenv("WINESTEAMNOEXEC", "1", 0); ++} + + /*********************************************************************** + * start_main_thread +@@ -2041,6 +2041,7 @@ static void start_main_thread(void) + signal_alloc_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ hacks_init(); + fsync_init(); + esync_init(); + virtual_map_user_shared_data(); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index dbd3e645976..683c2a4f1c2 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -146,6 +146,9 @@ extern BOOL is_wow64 DECLSPEC_HIDDEN; + extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + #endif + ++extern BOOL ac_odyssey DECLSPEC_HIDDEN; ++extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++ + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; + extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER_PROCESS_PARAMETERS *params, +diff --git a/include/config.h.in b/include/config.h.in +index 8f9b9737b24..4670e4bc881 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -188,6 +188,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_FILTER_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_FUTEX_H ++ + /* Define if Linux-style gethostbyname_r and gethostbyaddr_r are available */ + #undef HAVE_LINUX_GETHOSTBYNAME_R_6 + +diff --git a/server/fsync.c b/server/fsync.c +index af1c68f2a90..2b8c5e4bc15 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -44,21 +44,11 @@ + #include "fsync.h" + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; +-#endif +- int val; +-}; + #include "poppack.h" + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) +-{ +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); +-} ++#ifndef __NR_futex_waitv ++#define __NR_futex_waitv 449 ++#endif + + int do_fsync(void) + { +@@ -67,8 +57,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ fprintf( stderr, "fsync: old futex2 patches detected, disabling.\n" ); ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; + } + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-bae3769.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-bae3769.patch new file mode 100644 index 000000000..71033e3bc --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-bae3769.patch @@ -0,0 +1,917 @@ +From 04904bbab87d5662e40743f26534a74ee7d2c943 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 16 Mar 2022 11:50:28 +0100 +Subject: Import futex_waitv support from Proton Experimental + https://github.com/ValveSoftware/wine/tree/205874085341305cac84ae61a0141d2fa5b892dd + From ntdll/fsync: Support futex_waitv() API to ntdll: Include linux/futex.h in fsync.c + + +diff --git a/configure b/configure +index 7ea8b1c0ceb..03533857085 100755 +--- a/configure ++++ b/configure +@@ -7982,6 +7982,12 @@ if test "x$ac_cv_header_linux_filter_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/futex.h" "ac_cv_header_linux_futex_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_futex_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_FUTEX_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "linux/hdreg.h" "ac_cv_header_linux_hdreg_h" "$ac_includes_default" + if test "x$ac_cv_header_linux_hdreg_h" = xyes +diff --git a/configure.ac b/configure.ac +index 815e40fa08d..0082bd7e4a7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -441,6 +441,7 @@ AC_CHECK_HEADERS(\ + link.h \ + linux/cdrom.h \ + linux/filter.h \ ++ linux/futex.h \ + linux/hdreg.h \ + linux/hidraw.h \ + linux/input.h \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..0c6c5b8553b 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -953,6 +953,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); + if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); +@@ -968,6 +970,12 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ { ++ usleep( 0 ); ++ if (!event->signaled) ++ break; ++ } + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } +@@ -996,6 +1004,9 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + while (1) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + ret = do_poll( fds, pollcount, timeout ? &end : NULL ); + if (ret > 0) + { +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index d0f8be6c309..f9f6e252b6c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -5363,6 +5363,230 @@ static NTSTATUS set_pending_write( HANDLE device ) + return status; + } + ++static pthread_mutex_t async_file_read_mutex = PTHREAD_MUTEX_INITIALIZER; ++static pthread_cond_t async_file_read_cond = PTHREAD_COND_INITIALIZER; ++ ++struct async_file_read_job ++{ ++ HANDLE handle; ++ int unix_handle; ++ int needs_close; ++ HANDLE event; ++ IO_STATUS_BLOCK *io; ++ void *buffer; ++ ULONG length; ++ LARGE_INTEGER offset; ++ DWORD thread_id; ++ LONG cancelled; ++ struct list queue_entry; ++ struct async_file_read_job *next; ++}; ++ ++ ++static struct list async_file_read_queue = LIST_INIT( async_file_read_queue ); ++static struct async_file_read_job *async_file_read_running, *async_file_read_free; ++ ++static void async_file_complete_io( struct async_file_read_job *job, NTSTATUS status, ULONG total ) ++{ ++ job->io->Status = status; ++ job->io->Information = total; ++ ++ if (job->event) NtSetEvent( job->event, NULL ); ++} ++ ++static void *async_file_read_thread(void *dummy) ++{ ++ struct async_file_read_job *job, *ptr; ++ ULONG buffer_length = 0; ++ void *buffer = NULL; ++ struct list *entry; ++ NTSTATUS status; ++ ULONG total; ++ int result; ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ while (1) ++ { ++ while (!(entry = list_head( &async_file_read_queue ))) ++ { ++ pthread_cond_wait( &async_file_read_cond, &async_file_read_mutex ); ++ continue; ++ } ++ ++ job = LIST_ENTRY( entry, struct async_file_read_job, queue_entry ); ++ list_remove( entry ); ++ ++ total = 0; ++ ++ if ( job->cancelled ) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ status = STATUS_CANCELLED; ++ goto done; ++ } ++ ++ job->next = async_file_read_running; ++ async_file_read_running = job; ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ if (!buffer_length) ++ { ++ buffer = malloc(job->length); ++ buffer_length = job->length; ++ } ++ else if (buffer_length < job->length) ++ { ++ buffer = realloc(buffer, job->length); ++ buffer_length = job->length; ++ } ++ ++ while ((result = pread( job->unix_handle, buffer, job->length, job->offset.QuadPart )) == -1) ++ { ++ if (errno != EINTR) ++ { ++ status = errno_to_status( errno ); ++ goto done; ++ } ++ if (job->cancelled) ++ break; ++ } ++ ++ total = result; ++ status = (total || !job->length) ? STATUS_SUCCESS : STATUS_END_OF_FILE; ++done: ++ if (job->needs_close) close( job->unix_handle ); ++ ++ if (!InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ if (status == STATUS_SUCCESS) ++ memcpy( job->buffer, buffer, total ); ++ ++ async_file_complete_io( job, status, total ); ++ } ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (status != STATUS_CANCELLED) ++ { ++ ptr = async_file_read_running; ++ if (job == ptr) ++ { ++ async_file_read_running = job->next; ++ } ++ else ++ { ++ while (ptr && ptr->next != job) ++ ptr = ptr->next; ++ ++ assert( ptr ); ++ ptr->next = job->next; ++ } ++ } ++ ++ job->next = async_file_read_free; ++ async_file_read_free = job; ++ } ++ ++ return NULL; ++} ++ ++static pthread_once_t async_file_read_once = PTHREAD_ONCE_INIT; ++ ++static void async_file_read_init(void) ++{ ++ pthread_t async_file_read_thread_id; ++ pthread_attr_t pthread_attr; ++ ++ ERR("HACK: AC Odyssey async read workaround.\n"); ++ ++ pthread_attr_init( &pthread_attr ); ++ pthread_attr_setscope( &pthread_attr, PTHREAD_SCOPE_SYSTEM ); ++ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED ); ++ ++ pthread_create( &async_file_read_thread_id, &pthread_attr, (void * (*)(void *))async_file_read_thread, NULL); ++ pthread_attr_destroy( &pthread_attr ); ++} ++ ++static NTSTATUS queue_async_file_read( HANDLE handle, int unix_handle, int needs_close, HANDLE event, ++ IO_STATUS_BLOCK *io, void *buffer, ULONG length, LARGE_INTEGER *offset ) ++{ ++ struct async_file_read_job *job; ++ ++ pthread_once( &async_file_read_once, async_file_read_init ); ++ ++ NtResetEvent( event, NULL ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (async_file_read_free) ++ { ++ job = async_file_read_free; ++ async_file_read_free = async_file_read_free->next; ++ } ++ else ++ { ++ if (!(job = malloc( sizeof(*job) ))) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return STATUS_NO_MEMORY; ++ } ++ } ++ ++ job->handle = handle; ++ job->unix_handle = unix_handle; ++ job->needs_close = needs_close; ++ job->event = event; ++ job->io = io; ++ job->buffer = buffer; ++ job->length = length; ++ job->offset = *offset; ++ job->thread_id = GetCurrentThreadId(); ++ job->cancelled = 0; ++ ++ list_add_tail( &async_file_read_queue, &job->queue_entry ); ++ ++ pthread_cond_signal( &async_file_read_cond ); ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS cancel_async_file_read( HANDLE handle, IO_STATUS_BLOCK *io ) ++{ ++ DWORD thread_id = GetCurrentThreadId(); ++ struct async_file_read_job *job; ++ unsigned int count = 0; ++ ++ TRACE( "handle %p, io %p.\n", handle, io ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ job = async_file_read_running; ++ while (job) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ job = job->next; ++ } ++ ++ LIST_FOR_EACH_ENTRY( job, &async_file_read_queue, struct async_file_read_job, queue_entry ) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ } ++ ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return count ? STATUS_SUCCESS : STATUS_NOT_FOUND; ++} + + /****************************************************************************** + * NtReadFile (NTDLL.@) +@@ -5404,6 +5628,13 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo + goto done; + } + ++ if (ac_odyssey && async_read && length && event && !apc) ++ { ++ status = queue_async_file_read( handle, unix_handle, needs_close, event, io, buffer, length, offset ); ++ needs_close = 0; ++ goto err; ++ } ++ + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + { + /* async I/O doesn't make sense on regular files */ +@@ -6823,6 +7054,9 @@ NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status ) + + TRACE( "%p %p\n", handle, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, NULL )) ++ return (io_status->Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -6848,6 +7082,9 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_ + + TRACE( "%p %p %p\n", handle, io, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, io )) ++ return (io_status->Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index f5ae0a821b0..284e2fcce01 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -27,6 +27,9 @@ + #include + #include + #include ++#ifdef HAVE_LINUX_FUTEX_H ++# include ++#endif + #include + #include + #include +@@ -39,6 +42,7 @@ + # include + #endif + #include ++#include + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -54,43 +58,82 @@ + WINE_DEFAULT_DEBUG_CHANNEL(fsync); + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; ++#include "poppack.h" ++ ++/* futex_waitv interface */ ++ ++#ifndef __NR_futex_waitv ++# define __NR_futex_waitv 449 + #endif +- int val; +- int bitset; ++ ++#ifndef FUTEX_32 ++# define FUTEX_32 2 ++struct futex_waitv { ++ uint64_t val; ++ uint64_t uaddr; ++ uint32_t flags; ++ uint32_t __reserved; + }; +-#include "poppack.h" ++#endif ++ ++#define u64_to_ptr(x) (void *)(uintptr_t)(x) + +-static inline void small_pause(void) ++struct timespec64 + { +-#if defined(__i386__) || defined(__x86_64__) +- __asm__ __volatile__( "rep;nop" : : : "memory" ); +-#else +- __asm__ __volatile__( "" : : : "memory" ); +-#endif ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; + } + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) ++static inline void futex_vector_set( struct futex_waitv *waitv, int *addr, int val ) + { +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++ waitv->uaddr = (uintptr_t) addr; ++ waitv->val = val; ++ waitv->flags = FUTEX_32; ++ waitv->__reserved = 0; + } + +-static inline int futex_wake( int *addr, int val ) ++static void simulate_sched_quantum(void) + { +- return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++ if (!fsync_simulate_sched_quantum) return; ++ /* futex wait is often very quick to resume a waiting thread when woken. ++ * That reveals synchonization bugs in some games which happen to work on ++ * Windows due to the waiting threads having some minimal delay to wake up. */ ++ usleep(0); + } + +-static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++static inline int futex_wait_multiple( const struct futex_waitv *futexes, ++ int count, const ULONGLONG *end ) + { +- return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++ if (end) ++ { ++ struct timespec64 timeout; ++ ULONGLONG tmp = *end - SECS_1601_TO_1970 * TICKSPERSEC; ++ timeout.tv_sec = tmp / (ULONGLONG)TICKSPERSEC; ++ timeout.tv_nsec = (tmp % TICKSPERSEC) * 100; ++ ++ return syscall( __NR_futex_waitv, futexes, count, 0, &timeout, CLOCK_REALTIME ); ++ } ++ else ++ { ++ return syscall( __NR_futex_waitv, futexes, count, 0, NULL, 0 ); ++ } + } + +-static unsigned int spincount = 100; ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} + + int do_fsync(void) + { +@@ -99,11 +142,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; +- if (getenv("WINEFSYNC_SPINCOUNT")) +- spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); + } + + return do_fsync_cached; +@@ -646,64 +694,30 @@ NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) + return STATUS_SUCCESS; + } + +-static LONGLONG update_timeout( ULONGLONG end ) +-{ +- LARGE_INTEGER now; +- LONGLONG timeleft; +- +- NtQuerySystemTime( &now ); +- timeleft = end - now.QuadPart; +- if (timeleft < 0) timeleft = 0; +- return timeleft; +-} +- + static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) + { ++ struct futex_waitv futexes[2]; + int ret; + ++ futex_vector_set( &futexes[0], addr, val ); ++ + if (alertable) + { + int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; +- struct futex_wait_block futexes[2]; + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + +- futexes[0].addr = addr; +- futexes[0].val = val; +- futexes[1].addr = apc_futex; +- futexes[1].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[0].pad = futexes[1].pad = 0; +-#endif +- futexes[0].bitset = futexes[1].bitset = ~0; ++ futex_vector_set( &futexes[1], apc_futex, 0 ); + +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait_multiple( futexes, 2, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, 2, NULL ); ++ ret = futex_wait_multiple( futexes, 2, end ); + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + } + else + { +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait( addr, val, &tmo_p ); +- } +- else +- ret = futex_wait( addr, val, NULL ); ++ ret = futex_wait_multiple( futexes, 1, end ); + } + + if (!ret) +@@ -719,12 +733,11 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + static const LARGE_INTEGER zero = {0}; + +- struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct futex_waitv futexes[MAXIMUM_WAIT_OBJECTS + 1]; + struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ BOOL msgwait = FALSE, waited = FALSE; + int has_fsync = 0, has_server = 0; +- BOOL msgwait = FALSE; + int dummy_futex = 0; +- unsigned int spin; + LONGLONG timeleft; + LARGE_INTEGER now; + DWORD waitcount; +@@ -834,23 +847,15 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + struct semaphore *semaphore = obj->shm; + int current; + +- /* It would be a little clearer (and less error-prone) +- * to use a dedicated interlocked_dec_if_nonzero() +- * helper, but nesting loops like that is probably not +- * great for performance... */ +- for (spin = 0; spin <= spincount || current; ++spin) ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) + { +- if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) +- && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &semaphore->count; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &semaphore->count, 0 ); + break; + } + case FSYNC_MUTEX: +@@ -862,28 +867,25 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + mutex->count++; ++ if (waited) simulate_sched_quantum(); + return i; + } + +- for (spin = 0; spin <= spincount; ++spin) ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) + { +- if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return i; +- } +- else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) +- { +- TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return STATUS_ABANDONED_WAIT_0 + i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ if (waited) simulate_sched_quantum(); ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; + } + +- futexes[i].addr = &mutex->tid; +- futexes[i].val = tid; ++ futex_vector_set( &futexes[i], &mutex->tid, tid ); + break; + } + case FSYNC_AUTO_EVENT: +@@ -891,18 +893,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) + { +- if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + case FSYNC_MANUAL_EVENT: +@@ -911,18 +912,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + { +- if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + default: +@@ -933,31 +933,22 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + else + { + /* Avoid breaking things entirely. */ +- futexes[i].addr = &dummy_futex; +- futexes[i].val = dummy_futex; ++ futex_vector_set( &futexes[i], &dummy_futex, dummy_futex ); + } +- +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; + } + + if (alertable) + { + /* We already checked if it was signaled; don't bother doing it again. */ +- futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; +- futexes[i].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; +- i++; ++ futex_vector_set( &futexes[i++], ntdll_get_thread_data()->fsync_apc_futex, 0 ); + } + waitcount = i; + + /* Looks like everything is contended, so wait. */ + ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + if (timeout && !timeout->QuadPart) + { + /* Unlike esync, we already know that we've timed out, so we +@@ -965,17 +956,8 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } +- else if (timeout) +- { +- LONGLONG timeleft = update_timeout( end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + +- ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ret = futex_wait_multiple( futexes, waitcount, timeout ? &end : NULL ); + + /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, + * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to +@@ -986,6 +968,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } ++ else waited = TRUE; + } /* while (1) */ + } + else +@@ -1090,6 +1073,7 @@ tryagain: + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +@@ -1109,7 +1093,10 @@ tryagain: + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; +- if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ int current; ++ ++ if (!(current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ || __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) != current) + goto tooslow; + break; + } +@@ -1151,6 +1138,7 @@ tooslow: + for (--i; i >= 0; i--) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 0360bc64b00..f30d900a07b 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2028,6 +2028,34 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = + }; + + #endif /* _WIN64 */ ++ ++BOOL ac_odyssey; ++BOOL fsync_simulate_sched_quantum; ++ ++static void hacks_init(void) ++{ ++ static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe"; ++ static const char ac_odyssey_exe[] = "ACOdyssey.exe"; ++ const char *env_str; ++ ++ if (main_argc > 1 && strstr(main_argv[1], ac_odyssey_exe)) ++ { ++ ERR("HACK: AC Odyssey sync tweak on.\n"); ++ ac_odyssey = TRUE; ++ return; ++ } ++ env_str = getenv("WINE_FSYNC_SIMULATE_SCHED_QUANTUM"); ++ if (env_str) ++ fsync_simulate_sched_quantum = !!atoi(env_str); ++ else if (main_argc > 1) ++ fsync_simulate_sched_quantum = !!strstr(main_argv[1], upc_exe); ++ if (fsync_simulate_sched_quantum) ++ ERR("HACK: Simulating sched quantum in fsync.\n"); ++ ++ env_str = getenv("SteamGameId"); ++ if (env_str && !strcmp(env_str, "50130")) ++ setenv("WINESTEAMNOEXEC", "1", 0); ++} + + /*********************************************************************** + * start_main_thread +@@ -2041,6 +2041,7 @@ static void start_main_thread(void) + signal_alloc_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ hacks_init(); + fsync_init(); + esync_init(); + virtual_map_user_shared_data(); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index dbd3e645976..683c2a4f1c2 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -146,6 +146,9 @@ #ifdef __i386__ + extern struct ldt_copy __wine_ldt_copy; + #endif + ++extern BOOL ac_odyssey; ++extern BOOL fsync_simulate_sched_quantum; ++ + extern void init_environment( int argc, char *argv[], char *envp[] ); + extern void init_startup_info(void); + extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER_PROCESS_PARAMETERS *params, +diff --git a/include/config.h.in b/include/config.h.in +index 8f9b9737b24..4670e4bc881 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -188,6 +188,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_FILTER_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_FUTEX_H ++ + /* Define if Linux-style gethostbyname_r and gethostbyaddr_r are available */ + #undef HAVE_LINUX_GETHOSTBYNAME_R_6 + +diff --git a/server/fsync.c b/server/fsync.c +index af1c68f2a90..2b8c5e4bc15 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -44,21 +44,11 @@ + #include "fsync.h" + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; +-#endif +- int val; +-}; + #include "poppack.h" + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) +-{ +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); +-} ++#ifndef __NR_futex_waitv ++#define __NR_futex_waitv 449 ++#endif + + int do_fsync(void) + { +@@ -67,8 +57,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ fprintf( stderr, "fsync: old futex2 patches detected, disabling.\n" ); ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; + } + diff --git a/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-d5f2344.patch b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-d5f2344.patch new file mode 100644 index 000000000..8ade4eb54 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/fsync/legacy/fsync_futex_waitv-d5f2344.patch @@ -0,0 +1,917 @@ +From 04904bbab87d5662e40743f26534a74ee7d2c943 Mon Sep 17 00:00:00 2001 +From: Tk-Glitch +Date: Wed, 16 Mar 2022 11:50:28 +0100 +Subject: Import futex_waitv support from Proton Experimental + https://github.com/ValveSoftware/wine/tree/205874085341305cac84ae61a0141d2fa5b892dd + From ntdll/fsync: Support futex_waitv() API to ntdll: Include linux/futex.h in fsync.c + + +diff --git a/configure b/configure +index 7ea8b1c0ceb..03533857085 100755 +--- a/configure ++++ b/configure +@@ -7982,6 +7982,12 @@ if test "x$ac_cv_header_linux_filter_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/futex.h" "ac_cv_header_linux_futex_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_futex_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_FUTEX_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "linux/hdreg.h" "ac_cv_header_linux_hdreg_h" "$ac_includes_default" + if test "x$ac_cv_header_linux_hdreg_h" = xyes +diff --git a/configure.ac b/configure.ac +index 815e40fa08d..0082bd7e4a7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -441,6 +441,7 @@ AC_CHECK_HEADERS(\ + link.h \ + linux/cdrom.h \ + linux/filter.h \ ++ linux/futex.h \ + linux/hdreg.h \ + linux/hidraw.h \ + linux/input.h \ +diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c +index 3b97bfd62ad..0c6c5b8553b 100644 +--- a/dlls/ntdll/unix/esync.c ++++ b/dlls/ntdll/unix/esync.c +@@ -953,6 +953,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); + if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value)) + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); +@@ -968,6 +970,12 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + if (event->signaled) + { ++ if (ac_odyssey && alertable) ++ { ++ usleep( 0 ); ++ if (!event->signaled) ++ break; ++ } + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + return i; + } +@@ -996,6 +1004,9 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEA + + while (1) + { ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + ret = do_poll( fds, pollcount, timeout ? &end : NULL ); + if (ret > 0) + { +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index d0f8be6c309..f9f6e252b6c 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -5363,6 +5363,230 @@ static NTSTATUS set_pending_write( HANDLE device ) + return status; + } + ++static pthread_mutex_t async_file_read_mutex = PTHREAD_MUTEX_INITIALIZER; ++static pthread_cond_t async_file_read_cond = PTHREAD_COND_INITIALIZER; ++ ++struct async_file_read_job ++{ ++ HANDLE handle; ++ int unix_handle; ++ int needs_close; ++ HANDLE event; ++ IO_STATUS_BLOCK *io; ++ void *buffer; ++ ULONG length; ++ LARGE_INTEGER offset; ++ DWORD thread_id; ++ LONG cancelled; ++ struct list queue_entry; ++ struct async_file_read_job *next; ++}; ++ ++ ++static struct list async_file_read_queue = LIST_INIT( async_file_read_queue ); ++static struct async_file_read_job *async_file_read_running, *async_file_read_free; ++ ++static void async_file_complete_io( struct async_file_read_job *job, NTSTATUS status, ULONG total ) ++{ ++ job->io->u.Status = status; ++ job->io->Information = total; ++ ++ if (job->event) NtSetEvent( job->event, NULL ); ++} ++ ++static void *async_file_read_thread(void *dummy) ++{ ++ struct async_file_read_job *job, *ptr; ++ ULONG buffer_length = 0; ++ void *buffer = NULL; ++ struct list *entry; ++ NTSTATUS status; ++ ULONG total; ++ int result; ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ while (1) ++ { ++ while (!(entry = list_head( &async_file_read_queue ))) ++ { ++ pthread_cond_wait( &async_file_read_cond, &async_file_read_mutex ); ++ continue; ++ } ++ ++ job = LIST_ENTRY( entry, struct async_file_read_job, queue_entry ); ++ list_remove( entry ); ++ ++ total = 0; ++ ++ if ( job->cancelled ) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ status = STATUS_CANCELLED; ++ goto done; ++ } ++ ++ job->next = async_file_read_running; ++ async_file_read_running = job; ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ if (!buffer_length) ++ { ++ buffer = malloc(job->length); ++ buffer_length = job->length; ++ } ++ else if (buffer_length < job->length) ++ { ++ buffer = realloc(buffer, job->length); ++ buffer_length = job->length; ++ } ++ ++ while ((result = pread( job->unix_handle, buffer, job->length, job->offset.QuadPart )) == -1) ++ { ++ if (errno != EINTR) ++ { ++ status = errno_to_status( errno ); ++ goto done; ++ } ++ if (job->cancelled) ++ break; ++ } ++ ++ total = result; ++ status = (total || !job->length) ? STATUS_SUCCESS : STATUS_END_OF_FILE; ++done: ++ if (job->needs_close) close( job->unix_handle ); ++ ++ if (!InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ if (status == STATUS_SUCCESS) ++ memcpy( job->buffer, buffer, total ); ++ ++ async_file_complete_io( job, status, total ); ++ } ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (status != STATUS_CANCELLED) ++ { ++ ptr = async_file_read_running; ++ if (job == ptr) ++ { ++ async_file_read_running = job->next; ++ } ++ else ++ { ++ while (ptr && ptr->next != job) ++ ptr = ptr->next; ++ ++ assert( ptr ); ++ ptr->next = job->next; ++ } ++ } ++ ++ job->next = async_file_read_free; ++ async_file_read_free = job; ++ } ++ ++ return NULL; ++} ++ ++static pthread_once_t async_file_read_once = PTHREAD_ONCE_INIT; ++ ++static void async_file_read_init(void) ++{ ++ pthread_t async_file_read_thread_id; ++ pthread_attr_t pthread_attr; ++ ++ ERR("HACK: AC Odyssey async read workaround.\n"); ++ ++ pthread_attr_init( &pthread_attr ); ++ pthread_attr_setscope( &pthread_attr, PTHREAD_SCOPE_SYSTEM ); ++ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED ); ++ ++ pthread_create( &async_file_read_thread_id, &pthread_attr, (void * (*)(void *))async_file_read_thread, NULL); ++ pthread_attr_destroy( &pthread_attr ); ++} ++ ++static NTSTATUS queue_async_file_read( HANDLE handle, int unix_handle, int needs_close, HANDLE event, ++ IO_STATUS_BLOCK *io, void *buffer, ULONG length, LARGE_INTEGER *offset ) ++{ ++ struct async_file_read_job *job; ++ ++ pthread_once( &async_file_read_once, async_file_read_init ); ++ ++ NtResetEvent( event, NULL ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ ++ if (async_file_read_free) ++ { ++ job = async_file_read_free; ++ async_file_read_free = async_file_read_free->next; ++ } ++ else ++ { ++ if (!(job = malloc( sizeof(*job) ))) ++ { ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return STATUS_NO_MEMORY; ++ } ++ } ++ ++ job->handle = handle; ++ job->unix_handle = unix_handle; ++ job->needs_close = needs_close; ++ job->event = event; ++ job->io = io; ++ job->buffer = buffer; ++ job->length = length; ++ job->offset = *offset; ++ job->thread_id = GetCurrentThreadId(); ++ job->cancelled = 0; ++ ++ list_add_tail( &async_file_read_queue, &job->queue_entry ); ++ ++ pthread_cond_signal( &async_file_read_cond ); ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ ++ return STATUS_PENDING; ++} ++ ++static NTSTATUS cancel_async_file_read( HANDLE handle, IO_STATUS_BLOCK *io ) ++{ ++ DWORD thread_id = GetCurrentThreadId(); ++ struct async_file_read_job *job; ++ unsigned int count = 0; ++ ++ TRACE( "handle %p, io %p.\n", handle, io ); ++ ++ pthread_mutex_lock( &async_file_read_mutex ); ++ job = async_file_read_running; ++ while (job) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ job = job->next; ++ } ++ ++ LIST_FOR_EACH_ENTRY( job, &async_file_read_queue, struct async_file_read_job, queue_entry ) ++ { ++ if (((io && job->io == io) ++ || (!io && job->handle == handle && job->thread_id == thread_id)) ++ && !InterlockedCompareExchange(&job->cancelled, 1, 0)) ++ { ++ async_file_complete_io( job, STATUS_CANCELLED, 0 ); ++ ++count; ++ } ++ } ++ ++ pthread_mutex_unlock( &async_file_read_mutex ); ++ return count ? STATUS_SUCCESS : STATUS_NOT_FOUND; ++} + + /****************************************************************************** + * NtReadFile (NTDLL.@) +@@ -5404,6 +5628,13 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo + goto done; + } + ++ if (ac_odyssey && async_read && length && event && !apc) ++ { ++ status = queue_async_file_read( handle, unix_handle, needs_close, event, io, buffer, length, offset ); ++ needs_close = 0; ++ goto err; ++ } ++ + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + { + /* async I/O doesn't make sense on regular files */ +@@ -6823,6 +7054,9 @@ NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status ) + + TRACE( "%p %p\n", handle, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, NULL )) ++ return (io_status->u.Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -6848,6 +7082,9 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_ + + TRACE( "%p %p %p\n", handle, io, io_status ); + ++ if (ac_odyssey && !cancel_async_file_read( handle, io )) ++ return (io_status->u.Status = STATUS_SUCCESS); ++ + SERVER_START_REQ( cancel_async ) + { + req->handle = wine_server_obj_handle( handle ); +diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c +index f5ae0a821b0..284e2fcce01 100644 +--- a/dlls/ntdll/unix/fsync.c ++++ b/dlls/ntdll/unix/fsync.c +@@ -27,6 +27,9 @@ + #include + #include + #include ++#ifdef HAVE_LINUX_FUTEX_H ++# include ++#endif + #include + #include + #include +@@ -39,6 +42,7 @@ + # include + #endif + #include ++#include + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -54,43 +58,82 @@ + WINE_DEFAULT_DEBUG_CHANNEL(fsync); + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; ++#include "poppack.h" ++ ++/* futex_waitv interface */ ++ ++#ifndef __NR_futex_waitv ++# define __NR_futex_waitv 449 + #endif +- int val; +- int bitset; ++ ++#ifndef FUTEX_32 ++# define FUTEX_32 2 ++struct futex_waitv { ++ uint64_t val; ++ uint64_t uaddr; ++ uint32_t flags; ++ uint32_t __reserved; + }; +-#include "poppack.h" ++#endif ++ ++#define u64_to_ptr(x) (void *)(uintptr_t)(x) + +-static inline void small_pause(void) ++struct timespec64 + { +-#if defined(__i386__) || defined(__x86_64__) +- __asm__ __volatile__( "rep;nop" : : : "memory" ); +-#else +- __asm__ __volatile__( "" : : : "memory" ); +-#endif ++ long long tv_sec; ++ long long tv_nsec; ++}; ++ ++static LONGLONG update_timeout( ULONGLONG end ) ++{ ++ LARGE_INTEGER now; ++ LONGLONG timeleft; ++ ++ NtQuerySystemTime( &now ); ++ timeleft = end - now.QuadPart; ++ if (timeleft < 0) timeleft = 0; ++ return timeleft; + } + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) ++static inline void futex_vector_set( struct futex_waitv *waitv, int *addr, int val ) + { +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); ++ waitv->uaddr = (uintptr_t) addr; ++ waitv->val = val; ++ waitv->flags = FUTEX_32; ++ waitv->__reserved = 0; + } + +-static inline int futex_wake( int *addr, int val ) ++static void simulate_sched_quantum(void) + { +- return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++ if (!fsync_simulate_sched_quantum) return; ++ /* futex wait is often very quick to resume a waiting thread when woken. ++ * That reveals synchonization bugs in some games which happen to work on ++ * Windows due to the waiting threads having some minimal delay to wake up. */ ++ usleep(0); + } + +-static inline int futex_wait( int *addr, int val, struct timespec *timeout ) ++static inline int futex_wait_multiple( const struct futex_waitv *futexes, ++ int count, const ULONGLONG *end ) + { +- return syscall( __NR_futex, addr, 0, val, timeout, 0, 0 ); ++ if (end) ++ { ++ struct timespec64 timeout; ++ ULONGLONG tmp = *end - SECS_1601_TO_1970 * TICKSPERSEC; ++ timeout.tv_sec = tmp / (ULONGLONG)TICKSPERSEC; ++ timeout.tv_nsec = (tmp % TICKSPERSEC) * 100; ++ ++ return syscall( __NR_futex_waitv, futexes, count, 0, &timeout, CLOCK_REALTIME ); ++ } ++ else ++ { ++ return syscall( __NR_futex_waitv, futexes, count, 0, NULL, 0 ); ++ } + } + +-static unsigned int spincount = 100; ++static inline int futex_wake( int *addr, int val ) ++{ ++ return syscall( __NR_futex, addr, 1, val, NULL, 0, 0 ); ++} + + int do_fsync(void) + { +@@ -99,11 +142,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, NULL, 0, 0, NULL, 0 ); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; +- if (getenv("WINEFSYNC_SPINCOUNT")) +- spincount = atoi(getenv("WINEFSYNC_SPINCOUNT")); + } + + return do_fsync_cached; +@@ -646,64 +694,30 @@ NTSTATUS fsync_query_mutex( HANDLE handle, void *info, ULONG *ret_len ) + return STATUS_SUCCESS; + } + +-static LONGLONG update_timeout( ULONGLONG end ) +-{ +- LARGE_INTEGER now; +- LONGLONG timeleft; +- +- NtQuerySystemTime( &now ); +- timeleft = end - now.QuadPart; +- if (timeleft < 0) timeleft = 0; +- return timeleft; +-} +- + static NTSTATUS do_single_wait( int *addr, int val, ULONGLONG *end, BOOLEAN alertable ) + { ++ struct futex_waitv futexes[2]; + int ret; + ++ futex_vector_set( &futexes[0], addr, val ); ++ + if (alertable) + { + int *apc_futex = ntdll_get_thread_data()->fsync_apc_futex; +- struct futex_wait_block futexes[2]; + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + +- futexes[0].addr = addr; +- futexes[0].val = val; +- futexes[1].addr = apc_futex; +- futexes[1].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[0].pad = futexes[1].pad = 0; +-#endif +- futexes[0].bitset = futexes[1].bitset = ~0; ++ futex_vector_set( &futexes[1], apc_futex, 0 ); + +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait_multiple( futexes, 2, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, 2, NULL ); ++ ret = futex_wait_multiple( futexes, 2, end ); + + if (__atomic_load_n( apc_futex, __ATOMIC_SEQ_CST )) + return STATUS_USER_APC; + } + else + { +- if (end) +- { +- LONGLONG timeleft = update_timeout( *end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; +- ret = futex_wait( addr, val, &tmo_p ); +- } +- else +- ret = futex_wait( addr, val, NULL ); ++ ret = futex_wait_multiple( futexes, 1, end ); + } + + if (!ret) +@@ -719,12 +733,11 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + static const LARGE_INTEGER zero = {0}; + +- struct futex_wait_block futexes[MAXIMUM_WAIT_OBJECTS + 1]; ++ struct futex_waitv futexes[MAXIMUM_WAIT_OBJECTS + 1]; + struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; ++ BOOL msgwait = FALSE, waited = FALSE; + int has_fsync = 0, has_server = 0; +- BOOL msgwait = FALSE; + int dummy_futex = 0; +- unsigned int spin; + LONGLONG timeleft; + LARGE_INTEGER now; + DWORD waitcount; +@@ -834,23 +847,15 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + struct semaphore *semaphore = obj->shm; + int current; + +- /* It would be a little clearer (and less error-prone) +- * to use a dedicated interlocked_dec_if_nonzero() +- * helper, but nesting loops like that is probably not +- * great for performance... */ +- for (spin = 0; spin <= spincount || current; ++spin) ++ if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) + { +- if ((current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) +- && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &semaphore->count; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &semaphore->count, 0 ); + break; + } + case FSYNC_MUTEX: +@@ -862,28 +867,25 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + TRACE("Woken up by handle %p [%d].\n", handles[i], i); + mutex->count++; ++ if (waited) simulate_sched_quantum(); + return i; + } + +- for (spin = 0; spin <= spincount; ++spin) ++ if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) + { +- if (!(tid = __sync_val_compare_and_swap( &mutex->tid, 0, GetCurrentThreadId() ))) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return i; +- } +- else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) +- { +- TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); +- mutex->count = 1; +- return STATUS_ABANDONED_WAIT_0 + i; +- } +- small_pause(); ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ if (waited) simulate_sched_quantum(); ++ return i; ++ } ++ else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) ++ { ++ TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i); ++ mutex->count = 1; ++ return STATUS_ABANDONED_WAIT_0 + i; + } + +- futexes[i].addr = &mutex->tid; +- futexes[i].val = tid; ++ futex_vector_set( &futexes[i], &mutex->tid, tid ); + break; + } + case FSYNC_AUTO_EVENT: +@@ -891,18 +893,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) + { +- if (__sync_val_compare_and_swap( &event->signaled, 1, 0 )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + case FSYNC_MANUAL_EVENT: +@@ -911,18 +912,17 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + { + struct event *event = obj->shm; + +- for (spin = 0; spin <= spincount; ++spin) ++ if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) + { +- if (__atomic_load_n( &event->signaled, __ATOMIC_SEQ_CST )) +- { +- TRACE("Woken up by handle %p [%d].\n", handles[i], i); +- return i; +- } +- small_pause(); ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ ++ TRACE("Woken up by handle %p [%d].\n", handles[i], i); ++ if (waited) simulate_sched_quantum(); ++ return i; + } + +- futexes[i].addr = &event->signaled; +- futexes[i].val = 0; ++ futex_vector_set( &futexes[i], &event->signaled, 0 ); + break; + } + default: +@@ -933,31 +933,22 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + else + { + /* Avoid breaking things entirely. */ +- futexes[i].addr = &dummy_futex; +- futexes[i].val = dummy_futex; ++ futex_vector_set( &futexes[i], &dummy_futex, dummy_futex ); + } +- +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; + } + + if (alertable) + { + /* We already checked if it was signaled; don't bother doing it again. */ +- futexes[i].addr = ntdll_get_thread_data()->fsync_apc_futex; +- futexes[i].val = 0; +-#if __SIZEOF_POINTER__ == 4 +- futexes[i].pad = 0; +-#endif +- futexes[i].bitset = ~0; +- i++; ++ futex_vector_set( &futexes[i++], ntdll_get_thread_data()->fsync_apc_futex, 0 ); + } + waitcount = i; + + /* Looks like everything is contended, so wait. */ + ++ if (ac_odyssey && alertable) ++ usleep( 0 ); ++ + if (timeout && !timeout->QuadPart) + { + /* Unlike esync, we already know that we've timed out, so we +@@ -965,17 +956,8 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } +- else if (timeout) +- { +- LONGLONG timeleft = update_timeout( end ); +- struct timespec tmo_p; +- tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; +- tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100; + +- ret = futex_wait_multiple( futexes, waitcount, &tmo_p ); +- } +- else +- ret = futex_wait_multiple( futexes, waitcount, NULL ); ++ ret = futex_wait_multiple( futexes, waitcount, timeout ? &end : NULL ); + + /* FUTEX_WAIT_MULTIPLE can succeed or return -EINTR, -EAGAIN, + * -EFAULT/-EACCES, -ETIMEDOUT. In the first three cases we need to +@@ -986,6 +968,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, + TRACE("Wait timed out.\n"); + return STATUS_TIMEOUT; + } ++ else waited = TRUE; + } /* while (1) */ + } + else +@@ -1090,6 +1073,7 @@ tryagain: + for (i = 0; i < count; i++) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +@@ -1109,7 +1093,10 @@ tryagain: + case FSYNC_SEMAPHORE: + { + struct semaphore *semaphore = obj->shm; +- if (__sync_fetch_and_sub( &semaphore->count, 1 ) <= 0) ++ int current; ++ ++ if (!(current = __atomic_load_n( &semaphore->count, __ATOMIC_SEQ_CST )) ++ || __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) != current) + goto tooslow; + break; + } +@@ -1151,6 +1138,7 @@ tooslow: + for (--i; i >= 0; i--) + { + struct fsync *obj = objs[i]; ++ if (!obj) continue; + switch (obj->type) + { + case FSYNC_MUTEX: +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 0360bc64b00..f30d900a07b 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2028,6 +2028,34 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = + }; + + #endif /* _WIN64 */ ++ ++BOOL ac_odyssey; ++BOOL fsync_simulate_sched_quantum; ++ ++static void hacks_init(void) ++{ ++ static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe"; ++ static const char ac_odyssey_exe[] = "ACOdyssey.exe"; ++ const char *env_str; ++ ++ if (main_argc > 1 && strstr(main_argv[1], ac_odyssey_exe)) ++ { ++ ERR("HACK: AC Odyssey sync tweak on.\n"); ++ ac_odyssey = TRUE; ++ return; ++ } ++ env_str = getenv("WINE_FSYNC_SIMULATE_SCHED_QUANTUM"); ++ if (env_str) ++ fsync_simulate_sched_quantum = !!atoi(env_str); ++ else if (main_argc > 1) ++ fsync_simulate_sched_quantum = !!strstr(main_argv[1], upc_exe); ++ if (fsync_simulate_sched_quantum) ++ ERR("HACK: Simulating sched quantum in fsync.\n"); ++ ++ env_str = getenv("SteamGameId"); ++ if (env_str && !strcmp(env_str, "50130")) ++ setenv("WINESTEAMNOEXEC", "1", 0); ++} + + /*********************************************************************** + * start_main_thread +@@ -2041,6 +2041,7 @@ static void start_main_thread(void) + signal_alloc_thread( teb ); + dbg_init(); + startup_info_size = server_init_process(); ++ hacks_init(); + fsync_init(); + esync_init(); + virtual_map_user_shared_data(); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index dbd3e645976..683c2a4f1c2 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -146,6 +146,9 @@ extern BOOL is_wow64 DECLSPEC_HIDDEN; + extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; + #endif + ++extern BOOL ac_odyssey DECLSPEC_HIDDEN; ++extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; ++ + extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; + extern void init_startup_info(void) DECLSPEC_HIDDEN; + extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER_PROCESS_PARAMETERS *params, +diff --git a/include/config.h.in b/include/config.h.in +index 8f9b9737b24..4670e4bc881 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -188,6 +188,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_FILTER_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_FUTEX_H ++ + /* Define if Linux-style gethostbyname_r and gethostbyaddr_r are available */ + #undef HAVE_LINUX_GETHOSTBYNAME_R_6 + +diff --git a/server/fsync.c b/server/fsync.c +index af1c68f2a90..2b8c5e4bc15 100644 +--- a/server/fsync.c ++++ b/server/fsync.c +@@ -44,21 +44,11 @@ + #include "fsync.h" + + #include "pshpack4.h" +-struct futex_wait_block +-{ +- int *addr; +-#if __SIZEOF_POINTER__ == 4 +- int pad; +-#endif +- int val; +-}; + #include "poppack.h" + +-static inline int futex_wait_multiple( const struct futex_wait_block *futexes, +- int count, const struct timespec *timeout ) +-{ +- return syscall( __NR_futex, futexes, 31, count, timeout, 0, 0 ); +-} ++#ifndef __NR_futex_waitv ++#define __NR_futex_waitv 449 ++#endif + + int do_fsync(void) + { +@@ -67,8 +57,16 @@ int do_fsync(void) + + if (do_fsync_cached == -1) + { +- static const struct timespec zero; +- futex_wait_multiple( NULL, 0, &zero ); ++ FILE *f; ++ if ((f = fopen( "/sys/kernel/futex2/wait", "r" ))) ++ { ++ fclose(f); ++ do_fsync_cached = 0; ++ fprintf( stderr, "fsync: old futex2 patches detected, disabling.\n" ); ++ return do_fsync_cached; ++ } ++ ++ syscall( __NR_futex_waitv, 0, 0, 0, 0, 0); + do_fsync_cached = getenv("WINEFSYNC") && atoi(getenv("WINEFSYNC")) && errno != ENOSYS; + } + diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-bcrypt/proton-bcrypt b/wine-tkg-git/wine-tkg-patches/proton/proton-bcrypt/proton-bcrypt index 2320b9424..286c614c0 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/proton-bcrypt/proton-bcrypt +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-bcrypt/proton-bcrypt @@ -3,7 +3,7 @@ # Proton Bcrypt patches if [ "$_proton_bcrypt" = "true" ]; then if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 74c0da2d71e95f3e6bd6c8b440652933771b27d7 HEAD );then - if [ "$_use_staging" = "true" ] && ! grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition"; then + if [ "$_use_staging" = "true" ] && ( [ -e "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ] && ! grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ); then if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor b3cd21c21cf832529b154b3b8cc6ff85ec246c59 HEAD );then _patchname='proton-bcrypt-staging.patch' && _patchmsg="Using Proton Bcrypt patches" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 3db37dc5ae4e8b701c26f96fec97822b8b4da80c HEAD );then diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-tabtip/proton-tabtip b/wine-tkg-git/wine-tkg-patches/proton/proton-tabtip/proton-tabtip index 25df7e783..927b01c35 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/proton-tabtip/proton-tabtip +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-tabtip/proton-tabtip @@ -1,7 +1,7 @@ #!/bin/bash # tabtip - 7.2+ - if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_tabtip" = "true" ]; then + if [ "$_EXTERNAL_INSTALL" = "proton" ] && [ "$_tabtip" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor 5353b2594bf01b8b49c7d7510d0a21d52fae2544 HEAD ); then if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 26637de7cbc770cab20c6e35a828cc9e93fcfc9d HEAD ); then _patchname='proton-tabtip.patch' && _patchmsg="Enable Proton's SteamDeck additions" && nonuser_patcher elif ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor bf42dca35f05bce9996e91f59cc47b5a9e6996b2 HEAD ); then diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-956e580.patch b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-956e580.patch new file mode 100644 index 000000000..8c6c0c321 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-956e580.patch @@ -0,0 +1,397 @@ +From e9264df6e63b5df87d81e950675df7290ad43615 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jul 2020 14:57:28 -0500 +Subject: [PATCH] wineboot: On prefix upgrade, update win10 build number + +Some games (Death Stranding) require later build numbers than we had +shipped earlier. So fix it up on existing prefixes. +--- + programs/wineboot/wineboot.c | 38 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 569db5ee94d..a0a72040843 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1451,6 +1451,43 @@ static void update_user_profile(void) + LocalFree(sid); + } + ++static void update_win_version(void) ++{ ++ static const WCHAR win10_buildW[] = L"17763"; ++ ++ HKEY cv_h; ++ DWORD type, sz; ++ WCHAR current_version[256]; ++ ++ if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &cv_h) == ERROR_SUCCESS){ ++ /* get the current windows version */ ++ sz = sizeof(current_version); ++ if(RegQueryValueExW(cv_h, L"CurrentVersion", NULL, &type, (BYTE *)current_version, &sz) == ERROR_SUCCESS && ++ type == REG_SZ){ ++ if(!wcscmp(current_version, L"10.0")){ ++ RegSetValueExW(cv_h, L"CurrentBuild", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ RegSetValueExW(cv_h, L"CurrentBuildNumber", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ } ++ } ++ RegCloseKey(cv_h); ++ } ++ ++ if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &cv_h) == ERROR_SUCCESS){ ++ /* get the current windows version */ ++ sz = sizeof(current_version); ++ if(RegQueryValueExW(cv_h, L"CurrentVersion", NULL, &type, (BYTE *)current_version, &sz) == ERROR_SUCCESS && ++ type == REG_SZ){ ++ if(!wcscmp(current_version, L"10.0")){ ++ RegSetValueExW(cv_h, L"CurrentBuild", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ RegSetValueExW(cv_h, L"CurrentBuildNumber", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ } ++ } ++ RegCloseKey(cv_h); ++ } ++} ++ + /* execute rundll32 on the wine.inf file if necessary */ + static void update_wineprefix( BOOL force ) + { +@@ -1496,6 +1533,7 @@ static void update_wineprefix( BOOL force ) + } + install_root_pnp_devices(); + update_user_profile(); ++ update_win_version(); + + WINE_MESSAGE( "wine: configuration in %s has been updated.\n", debugstr_w(prettyprint_configdir()) ); + } +From e3f3c07144cda0c4aa25a3d104ca76c17e36bfdf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Sep 2021 23:03:54 +0300 +Subject: [PATCH] Bump current build number to 18363 (Win10 1909). + +CW-Bug-ID: #19427 + +For DeathLoop. +--- + dlls/kernel32/version.rc | 8 ++++---- + dlls/kernelbase/version.c | 2 +- + dlls/ntdll/unix/signal_x86_64.c | 4 ++-- + loader/wine.inf.in | 8 ++++---- + programs/wineboot/wineboot.c | 2 +- + 5 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc +index b6002f51f7a..c58acb8a8b6 100644 +--- a/dlls/kernel32/version.rc ++++ b/dlls/kernel32/version.rc +@@ -26,9 +26,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + #define WINE_FILENAME_STR "kernel32.dll" + + /* these values come from Windows 10 Version 1909 */ +-#define WINE_FILEVERSION 10,0,18362,1350 +-#define WINE_FILEVERSION_STR "10.0.18362.1350" +-#define WINE_PRODUCTVERSION 10,0,18362,1350 +-#define WINE_PRODUCTVERSION_STR "10.0.18362.1350" ++#define WINE_FILEVERSION 10,0,18363,900 ++#define WINE_FILEVERSION_STR "10.0.18363.900" ++#define WINE_PRODUCTVERSION 10,0,18363,900 ++#define WINE_PRODUCTVERSION_STR "10.0.18363.900" + + #include "wine/wine_common_ver.rc" +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index bd878cb324d..febb78d94e9 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -151,7 +151,7 @@ static const struct + }, + /* Windows 10 */ + { +- { 10, 0, 18362 }, ++ { 10, 0, 18363 }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } + }; +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4853db16e77..faba4be0b91 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2448,11 +2448,11 @@ HKLM,"System\CurrentControlSet\Services\Winsock\Parameters",,16 + HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Catalog_Entries",,16 + + [VersionInfo] +-HKLM,%CurrentVersionNT%,"CurrentVersion",2,"6.3" ++HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" + HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10001,10 + HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10001,0 +-HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18362" +-HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18362" ++HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" ++HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" + HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index f78d4bdae25..eb69a5941a9 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1706,7 +1706,7 @@ static void update_user_profile(void) + + static void update_win_version(void) + { +- static const WCHAR win10_buildW[] = L"17763"; ++ static const WCHAR win10_buildW[] = L"18363"; + + HKEY cv_h; + DWORD type, sz; +From 7381437f853f4a1706ff80aa74860fc5366f2fd0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 29 Sep 2021 14:39:06 +0300 +Subject: [PATCH] wine.inf: Add ReleaseId value to %CurrentVersionNT%. + +CW-Bug-Id: #19484 +--- + loader/wine.inf.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 2db2abab2d8..1cfe19dace6 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2423,6 +2423,7 @@ HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 + HKLM,%CurrentVersionNT%,"ProductName",2,"Windows 10 Pro" ++HKLM,%CurrentVersionNT%,"ReleaseId",,"1909" + HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" + HKLM,%Control%\Windows,"CSDVersion",0x10003,0 + HKLM,%Control%\Session Manager\Environment,"OS",2,"Windows_NT" +From 6de51bc22e5bd220ba79cbfae052b08abc4289d2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 14 Jan 2022 11:46:04 +0300 +Subject: [PATCH] Bump current build number to 19043 (Win10 2009). + +--- + dlls/kernel32/version.rc | 10 +++++----- + dlls/kernelbase/version.c | 2 +- + dlls/ntdll/unix/signal_x86_64.c | 3 ++- + dlls/ntdll/version.c | 2 +- + loader/wine.inf.in | 14 ++++++++------ + programs/wineboot/wineboot.c | 2 +- + 6 files changed, 18 insertions(+), 15 deletions(-) + +diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc +index c58acb8a8b6..f91262dfa8e 100644 +--- a/dlls/kernel32/version.rc ++++ b/dlls/kernel32/version.rc +@@ -25,10 +25,10 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + #define WINE_FILEDESCRIPTION_STR "Wine kernel DLL" + #define WINE_FILENAME_STR "kernel32.dll" + +-/* these values come from Windows 10 Version 1909 */ +-#define WINE_FILEVERSION 10,0,18363,900 +-#define WINE_FILEVERSION_STR "10.0.18363.900" +-#define WINE_PRODUCTVERSION 10,0,18363,900 +-#define WINE_PRODUCTVERSION_STR "10.0.18363.900" ++/* these values come from Windows 10 Version 2009 */ ++#define WINE_FILEVERSION 10,0,19043,1466 ++#define WINE_FILEVERSION_STR "10.0.19043.1466" ++#define WINE_PRODUCTVERSION 10,0,19043,1466 ++#define WINE_PRODUCTVERSION_STR "10.0.19043.1466" + + #include "wine/wine_common_ver.rc" +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index b4f9406f3c6..ab4ec32a260 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -158,7 +158,7 @@ static const struct + }, + /* Windows 10 */ + { +- { 10, 0, 18363 }, ++ { 10, 0, 19043 }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } + }; +diff --git a/dlls/ntdll/version.c b/dlls/ntdll/version.c +index 077ab65a502..6c846d0ac33 100644 +--- a/dlls/ntdll/version.c ++++ b/dlls/ntdll/version.c +@@ -167,7 +167,7 @@ static const RTL_OSVERSIONINFOEXW VersionData[NB_WINDOWS_VERSIONS] = + }, + /* WIN10 */ + { +- sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 18362, VER_PLATFORM_WIN32_NT, ++ sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 19043, VER_PLATFORM_WIN32_NT, + L"", 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 + }, + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 769ff984187..0a41810d884 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2456,8 +2456,8 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" + HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10001,10 + HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10001,0 +-HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" +-HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" ++HKLM,%CurrentVersionNT%,"CurrentBuild",2,"19043" ++HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" + HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +@@ -2466,8 +2466,9 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 + HKLM,%CurrentVersionNT%,"EditionId",2,"Professional" ++HKLM,%CurrentVersionNT%,"DisplayVersion",2,"21H1" + HKLM,%CurrentVersionNT%,"ProductName",2,"Windows 10 Pro" +-HKLM,%CurrentVersionNT%,"ReleaseId",,"1909" ++HKLM,%CurrentVersionNT%,"ReleaseId",,"2009" + HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" + HKLM,%Control%\Windows,"CSDVersion",0x10003,0 + HKLM,%Control%\Session Manager\Environment,"OS",2,"Windows_NT" +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 14d12883813..5d1190583b1 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1415,7 +1415,7 @@ static void update_user_profile(void) + + static void update_win_version(void) + { +- static const WCHAR win10_buildW[] = L"18363"; ++ static const WCHAR win10_buildW[] = L"19043"; + + HKEY cv_h; + DWORD type, sz; +From fc3d95a311a8e329af3d55a88fc68b41869af863 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 22 Dec 2021 00:57:25 +0300 +Subject: [PATCH] wine.inf: Set a valid Win10 ProductId. + +CW-Bug-Id: #19702 +--- + loader/wine.inf.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 0a41810d884..1c2286d722f 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -549,7 +549,7 @@ HKCU,%CurrentVersion%\Run,,16 + HKCU,%CurrentVersionNT%\Winlogon,,16 + HKLM,%CurrentVersion%,"CommonFilesDir",,"%16427%" + HKLM,%CurrentVersion%,"FirstInstallDateTime",1,21,81,7c,23 +-HKLM,%CurrentVersion%,"ProductId",,"12345-oem-0000001-54321" ++HKLM,%CurrentVersion%,"ProductId",,"00330-50000-00000-AAOEM" + HKLM,%CurrentVersion%,"ProgramFilesDir",,"%16422%" + HKLM,%CurrentVersion%,"ProgramFilesPath",0x20000,"%%ProgramFiles%%" + HKLM,%CurrentVersion%,"RegisteredOrganization",2,"" +@@ -573,7 +573,7 @@ HKLM,%CurrentVersion%\Setup\WindowsFeatures\WindowsMediaVersion,,,"12.0.7601.188 + HKLM,%CurrentVersion%\Shell Extensions\Approved,,16 + HKLM,%CurrentVersion%\Uninstall,,16 + HKLM,%CurrentVersionNT%,"InstallDate",0x10003,1273299354 +-HKLM,%CurrentVersionNT%,"ProductId",,"12345-oem-0000001-54321" ++HKLM,%CurrentVersionNT%,"ProductId",,"00330-50000-00000-AAOEM" + HKLM,%CurrentVersionNT%,"RegisteredOrganization",2,"" + HKLM,%CurrentVersionNT%,"RegisteredOwner",2,"" + HKLM,%CurrentVersionNT%,"SystemRoot",,"%10%" +From 80d7c0c041636e900589e8ae41c5fdd3a0470b29 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 22 Dec 2021 00:59:32 +0300 +Subject: [PATCH] wineboot: Generate better DigitalProductId. + +CW-Bug-Id: #19702 +--- + loader/wine.inf.in | 4 +-- + programs/wineboot/wineboot.c | 48 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 1c2286d722f..677ae91a406 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2456,7 +2456,7 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + HKLM,%CurrentVersionNT%,"CurrentBuild",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" +-HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ ++HKLM,%CurrentVersionNT%,"DigitalProductId",2,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 5d1190583b1..16b44ca28ab 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -68,6 +68,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1605,6 +1606,52 @@ static void usage( int status ) + exit( status ); + } + ++static void create_digitalproductid(void) ++{ ++ BYTE digital_product_id[0xa4]; ++ char product_id[256]; ++ LSTATUS status; ++ unsigned int i; ++ DWORD size; ++ DWORD type; ++ HKEY key; ++ ++ if ((status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &key ))) ++ return; ++ size = sizeof(product_id); ++ status = RegQueryValueExA( key, "ProductId", NULL, &type, (BYTE *)product_id, &size ); ++ if (status) goto done; ++ if (!size) goto done; ++ if (product_id[size - 1]) ++ { ++ if (size == sizeof(product_id)) goto done; ++ product_id[size++] = 0; ++ } ++ ++ if (!RegQueryValueExA( key, "DigitalProductId", NULL, &type, NULL, &size ) && size == sizeof(digital_product_id)) ++ { ++ if (RegQueryValueExA( key, "DigitalProductId", NULL, &type, digital_product_id, &size )) ++ goto done; ++ for (i = 0; i < size; ++i) ++ if (digital_product_id[i]) break; ++ if (i < size) goto done; ++ } ++ ++ memset( digital_product_id, 0, sizeof(digital_product_id) ); ++ *(DWORD *)digital_product_id = sizeof(digital_product_id); ++ digital_product_id[4] = 3; ++ strcpy( (char *)digital_product_id + 8, product_id ); ++ *(DWORD *)(digital_product_id + 0x20) = 0x0cec; ++ *(DWORD *)(digital_product_id + 0x34) = 0x0cec; ++ strcpy( (char *)digital_product_id + 0x24, "[TH] X19-99481" ); ++ digital_product_id[0x42] = 8; ++ RtlGenRandom( digital_product_id + 0x38, 0x18 ); ++ RegSetValueExA( key, "DigitalProductId", 0, REG_BINARY, digital_product_id, sizeof(digital_product_id) ); ++done: ++ RegCloseKey( key ); ++} ++ + int __cdecl main( int argc, char *argv[] ) + { + /* First, set the current directory to SystemRoot */ +@@ -1716,6 +1763,7 @@ int __cdecl main( int argc, char *argv[] ) + } + if (init || update) update_wineprefix( update ); + ++ create_digitalproductid(); + create_volatile_environment_registry_key(); + + ProcessRunKeys( HKEY_LOCAL_MACHINE, L"RunOnce", TRUE, TRUE ); diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-fd00d8e.patch b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-fd00d8e.patch new file mode 100644 index 000000000..a2c888136 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/legacy/proton-win10-default-fd00d8e.patch @@ -0,0 +1,397 @@ +From e9264df6e63b5df87d81e950675df7290ad43615 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Wed, 15 Jul 2020 14:57:28 -0500 +Subject: [PATCH] wineboot: On prefix upgrade, update win10 build number + +Some games (Death Stranding) require later build numbers than we had +shipped earlier. So fix it up on existing prefixes. +--- + programs/wineboot/wineboot.c | 38 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 569db5ee94d..a0a72040843 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1451,6 +1451,43 @@ static void update_user_profile(void) + LocalFree(sid); + } + ++static void update_win_version(void) ++{ ++ static const WCHAR win10_buildW[] = L"17763"; ++ ++ HKEY cv_h; ++ DWORD type, sz; ++ WCHAR current_version[256]; ++ ++ if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &cv_h) == ERROR_SUCCESS){ ++ /* get the current windows version */ ++ sz = sizeof(current_version); ++ if(RegQueryValueExW(cv_h, L"CurrentVersion", NULL, &type, (BYTE *)current_version, &sz) == ERROR_SUCCESS && ++ type == REG_SZ){ ++ if(!wcscmp(current_version, L"10.0")){ ++ RegSetValueExW(cv_h, L"CurrentBuild", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ RegSetValueExW(cv_h, L"CurrentBuildNumber", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ } ++ } ++ RegCloseKey(cv_h); ++ } ++ ++ if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &cv_h) == ERROR_SUCCESS){ ++ /* get the current windows version */ ++ sz = sizeof(current_version); ++ if(RegQueryValueExW(cv_h, L"CurrentVersion", NULL, &type, (BYTE *)current_version, &sz) == ERROR_SUCCESS && ++ type == REG_SZ){ ++ if(!wcscmp(current_version, L"10.0")){ ++ RegSetValueExW(cv_h, L"CurrentBuild", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ RegSetValueExW(cv_h, L"CurrentBuildNumber", 0, REG_SZ, (const BYTE *)win10_buildW, sizeof(win10_buildW)); ++ } ++ } ++ RegCloseKey(cv_h); ++ } ++} ++ + /* execute rundll32 on the wine.inf file if necessary */ + static void update_wineprefix( BOOL force ) + { +@@ -1496,6 +1533,7 @@ static void update_wineprefix( BOOL force ) + } + install_root_pnp_devices(); + update_user_profile(); ++ update_win_version(); + + WINE_MESSAGE( "wine: configuration in %s has been updated.\n", debugstr_w(prettyprint_configdir()) ); + } +From e3f3c07144cda0c4aa25a3d104ca76c17e36bfdf Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 14 Sep 2021 23:03:54 +0300 +Subject: [PATCH] Bump current build number to 18363 (Win10 1909). + +CW-Bug-ID: #19427 + +For DeathLoop. +--- + dlls/kernel32/version.rc | 8 ++++---- + dlls/kernelbase/version.c | 2 +- + dlls/ntdll/unix/signal_x86_64.c | 4 ++-- + loader/wine.inf.in | 8 ++++---- + programs/wineboot/wineboot.c | 2 +- + 5 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc +index b6002f51f7a..c58acb8a8b6 100644 +--- a/dlls/kernel32/version.rc ++++ b/dlls/kernel32/version.rc +@@ -26,9 +26,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + #define WINE_FILENAME_STR "kernel32.dll" + + /* these values come from Windows 10 Version 1909 */ +-#define WINE_FILEVERSION 10,0,18362,1350 +-#define WINE_FILEVERSION_STR "10.0.18362.1350" +-#define WINE_PRODUCTVERSION 10,0,18362,1350 +-#define WINE_PRODUCTVERSION_STR "10.0.18362.1350" ++#define WINE_FILEVERSION 10,0,18363,900 ++#define WINE_FILEVERSION_STR "10.0.18363.900" ++#define WINE_PRODUCTVERSION 10,0,18363,900 ++#define WINE_PRODUCTVERSION_STR "10.0.18363.900" + + #include "wine/wine_common_ver.rc" +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index bd878cb324d..febb78d94e9 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -151,7 +151,7 @@ static const struct + }, + /* Windows 10 */ + { +- { 10, 0, 18362 }, ++ { 10, 0, 18363 }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } + }; +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 4853db16e77..faba4be0b91 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2448,11 +2448,11 @@ HKLM,"System\CurrentControlSet\Services\Winsock\Parameters",,16 + HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Catalog_Entries",,16 + + [VersionInfo] +-HKLM,%CurrentVersionNT%,"CurrentVersion",2,"6.3" ++HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" + HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10003,10 + HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10003,0 +-HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18362" +-HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18362" ++HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" ++HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" + HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index f78d4bdae25..eb69a5941a9 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1706,7 +1706,7 @@ static void update_user_profile(void) + + static void update_win_version(void) + { +- static const WCHAR win10_buildW[] = L"17763"; ++ static const WCHAR win10_buildW[] = L"18363"; + + HKEY cv_h; + DWORD type, sz; +From 7381437f853f4a1706ff80aa74860fc5366f2fd0 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 29 Sep 2021 14:39:06 +0300 +Subject: [PATCH] wine.inf: Add ReleaseId value to %CurrentVersionNT%. + +CW-Bug-Id: #19484 +--- + loader/wine.inf.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 2db2abab2d8..1cfe19dace6 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2423,6 +2423,7 @@ HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 + HKLM,%CurrentVersionNT%,"ProductName",2,"Windows 10 Pro" ++HKLM,%CurrentVersionNT%,"ReleaseId",,"1909" + HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" + HKLM,%Control%\Windows,"CSDVersion",0x10003,0 + HKLM,%Control%\Session Manager\Environment,"OS",2,"Windows_NT" +From 6de51bc22e5bd220ba79cbfae052b08abc4289d2 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 14 Jan 2022 11:46:04 +0300 +Subject: [PATCH] Bump current build number to 19043 (Win10 2009). + +--- + dlls/kernel32/version.rc | 10 +++++----- + dlls/kernelbase/version.c | 2 +- + dlls/ntdll/unix/signal_x86_64.c | 3 ++- + dlls/ntdll/version.c | 2 +- + loader/wine.inf.in | 14 ++++++++------ + programs/wineboot/wineboot.c | 2 +- + 6 files changed, 18 insertions(+), 15 deletions(-) + +diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc +index c58acb8a8b6..f91262dfa8e 100644 +--- a/dlls/kernel32/version.rc ++++ b/dlls/kernel32/version.rc +@@ -25,10 +25,10 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + #define WINE_FILEDESCRIPTION_STR "Wine kernel DLL" + #define WINE_FILENAME_STR "kernel32.dll" + +-/* these values come from Windows 10 Version 1909 */ +-#define WINE_FILEVERSION 10,0,18363,900 +-#define WINE_FILEVERSION_STR "10.0.18363.900" +-#define WINE_PRODUCTVERSION 10,0,18363,900 +-#define WINE_PRODUCTVERSION_STR "10.0.18363.900" ++/* these values come from Windows 10 Version 2009 */ ++#define WINE_FILEVERSION 10,0,19043,1466 ++#define WINE_FILEVERSION_STR "10.0.19043.1466" ++#define WINE_PRODUCTVERSION 10,0,19043,1466 ++#define WINE_PRODUCTVERSION_STR "10.0.19043.1466" + + #include "wine/wine_common_ver.rc" +diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c +index b4f9406f3c6..ab4ec32a260 100644 +--- a/dlls/kernelbase/version.c ++++ b/dlls/kernelbase/version.c +@@ -158,7 +158,7 @@ static const struct + }, + /* Windows 10 */ + { +- { 10, 0, 18363 }, ++ { 10, 0, 19043 }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } + }; +diff --git a/dlls/ntdll/version.c b/dlls/ntdll/version.c +index 077ab65a502..6c846d0ac33 100644 +--- a/dlls/ntdll/version.c ++++ b/dlls/ntdll/version.c +@@ -167,7 +167,7 @@ static const RTL_OSVERSIONINFOEXW VersionData[NB_WINDOWS_VERSIONS] = + }, + /* WIN10 */ + { +- sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 18362, VER_PLATFORM_WIN32_NT, ++ sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 19043, VER_PLATFORM_WIN32_NT, + L"", 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 + }, + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 769ff984187..0a41810d884 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2456,8 +2456,8 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" + HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10003,10 + HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10003,0 +-HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" +-HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" ++HKLM,%CurrentVersionNT%,"CurrentBuild",2,"19043" ++HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" + HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +@@ -2466,8 +2466,9 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 + HKLM,%CurrentVersionNT%,"EditionId",2,"Professional" ++HKLM,%CurrentVersionNT%,"DisplayVersion",2,"21H1" + HKLM,%CurrentVersionNT%,"ProductName",2,"Windows 10 Pro" +-HKLM,%CurrentVersionNT%,"ReleaseId",,"1909" ++HKLM,%CurrentVersionNT%,"ReleaseId",,"2009" + HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" + HKLM,%Control%\Windows,"CSDVersion",0x10003,0 + HKLM,%Control%\Session Manager\Environment,"OS",2,"Windows_NT" +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 14d12883813..5d1190583b1 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -1415,7 +1415,7 @@ static void update_user_profile(void) + + static void update_win_version(void) + { +- static const WCHAR win10_buildW[] = L"18363"; ++ static const WCHAR win10_buildW[] = L"19043"; + + HKEY cv_h; + DWORD type, sz; +From fc3d95a311a8e329af3d55a88fc68b41869af863 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 22 Dec 2021 00:57:25 +0300 +Subject: [PATCH] wine.inf: Set a valid Win10 ProductId. + +CW-Bug-Id: #19702 +--- + loader/wine.inf.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 0a41810d884..1c2286d722f 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -549,7 +549,7 @@ HKCU,%CurrentVersion%\Run,,16 + HKCU,%CurrentVersionNT%\Winlogon,,16 + HKLM,%CurrentVersion%,"CommonFilesDir",,"%16427%" + HKLM,%CurrentVersion%,"FirstInstallDateTime",1,21,81,7c,23 +-HKLM,%CurrentVersion%,"ProductId",,"12345-oem-0000001-54321" ++HKLM,%CurrentVersion%,"ProductId",,"00330-50000-00000-AAOEM" + HKLM,%CurrentVersion%,"ProgramFilesDir",,"%16422%" + HKLM,%CurrentVersion%,"ProgramFilesPath",0x20000,"%%ProgramFiles%%" + HKLM,%CurrentVersion%,"RegisteredOrganization",2,"" +@@ -573,7 +573,7 @@ HKLM,%CurrentVersion%\Setup\WindowsFeatures\WindowsMediaVersion,,,"12.0.7601.188 + HKLM,%CurrentVersion%\Shell Extensions\Approved,,16 + HKLM,%CurrentVersion%\Uninstall,,16 + HKLM,%CurrentVersionNT%,"InstallDate",0x10003,1273299354 +-HKLM,%CurrentVersionNT%,"ProductId",,"12345-oem-0000001-54321" ++HKLM,%CurrentVersionNT%,"ProductId",,"00330-50000-00000-AAOEM" + HKLM,%CurrentVersionNT%,"RegisteredOrganization",2,"" + HKLM,%CurrentVersionNT%,"RegisteredOwner",2,"" + HKLM,%CurrentVersionNT%,"SystemRoot",,"%10%" +From 80d7c0c041636e900589e8ae41c5fdd3a0470b29 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 22 Dec 2021 00:59:32 +0300 +Subject: [PATCH] wineboot: Generate better DigitalProductId. + +CW-Bug-Id: #19702 +--- + loader/wine.inf.in | 4 +-- + programs/wineboot/wineboot.c | 48 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/loader/wine.inf.in b/loader/wine.inf.in +index 1c2286d722f..677ae91a406 100644 +--- a/loader/wine.inf.in ++++ b/loader/wine.inf.in +@@ -2456,7 +2456,7 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca + HKLM,%CurrentVersionNT%,"CurrentBuild",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"19043" + HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" +-HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ ++HKLM,%CurrentVersionNT%,"DigitalProductId",2,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c +index 5d1190583b1..16b44ca28ab 100644 +--- a/programs/wineboot/wineboot.c ++++ b/programs/wineboot/wineboot.c +@@ -68,6 +68,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1605,6 +1606,52 @@ static void usage( int status ) + exit( status ); + } + ++static void create_digitalproductid(void) ++{ ++ BYTE digital_product_id[0xa4]; ++ char product_id[256]; ++ LSTATUS status; ++ unsigned int i; ++ DWORD size; ++ DWORD type; ++ HKEY key; ++ ++ if ((status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", ++ 0, KEY_ALL_ACCESS, &key ))) ++ return; ++ size = sizeof(product_id); ++ status = RegQueryValueExA( key, "ProductId", NULL, &type, (BYTE *)product_id, &size ); ++ if (status) goto done; ++ if (!size) goto done; ++ if (product_id[size - 1]) ++ { ++ if (size == sizeof(product_id)) goto done; ++ product_id[size++] = 0; ++ } ++ ++ if (!RegQueryValueExA( key, "DigitalProductId", NULL, &type, NULL, &size ) && size == sizeof(digital_product_id)) ++ { ++ if (RegQueryValueExA( key, "DigitalProductId", NULL, &type, digital_product_id, &size )) ++ goto done; ++ for (i = 0; i < size; ++i) ++ if (digital_product_id[i]) break; ++ if (i < size) goto done; ++ } ++ ++ memset( digital_product_id, 0, sizeof(digital_product_id) ); ++ *(DWORD *)digital_product_id = sizeof(digital_product_id); ++ digital_product_id[4] = 3; ++ strcpy( (char *)digital_product_id + 8, product_id ); ++ *(DWORD *)(digital_product_id + 0x20) = 0x0cec; ++ *(DWORD *)(digital_product_id + 0x34) = 0x0cec; ++ strcpy( (char *)digital_product_id + 0x24, "[TH] X19-99481" ); ++ digital_product_id[0x42] = 8; ++ RtlGenRandom( digital_product_id + 0x38, 0x18 ); ++ RegSetValueExA( key, "DigitalProductId", 0, REG_BINARY, digital_product_id, sizeof(digital_product_id) ); ++done: ++ RegCloseKey( key ); ++} ++ + int __cdecl main( int argc, char *argv[] ) + { + /* First, set the current directory to SystemRoot */ +@@ -1716,6 +1763,7 @@ int __cdecl main( int argc, char *argv[] ) + } + if (init || update) update_wineprefix( update ); + ++ create_digitalproductid(); + create_volatile_environment_registry_key(); + + ProcessRunKeys( HKEY_LOCAL_MACHINE, L"RunOnce", TRUE, TRUE ); diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default index 85959a3af..e7a44aa13 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default @@ -2,8 +2,12 @@ # Set the default wine version to win10 if [ "$_win10_default" = "true" ] && [ "$_unfrog" != "true" ] && git merge-base --is-ancestor 74dc0c5df9c3094352caedda8ebe14ed2dfd615e HEAD; then - if git merge-base --is-ancestor e8b0c9239562ed78c94d7dba87d5004b4d82b113 HEAD; then + if git merge-base --is-ancestor fd00d8e4cc11e494745184897133aff8718316c8 HEAD; then _patchname='proton-win10-default.patch' && _patchmsg="Enforce win10 as default wine version" && nonuser_patcher + elif git merge-base --is-ancestor 956e580f0656a8191489d1fd354f3038d0ea1431 HEAD; then + _patchname='proton-win10-default-fd00d8e.patch' && _patchmsg="Enforce win10 as default wine version" && nonuser_patcher + elif git merge-base --is-ancestor e8b0c9239562ed78c94d7dba87d5004b4d82b113 HEAD; then + _patchname='proton-win10-default-956e580.patch' && _patchmsg="Enforce win10 as default wine version" && nonuser_patcher elif git merge-base --is-ancestor 863858da2aa42edfa3b4e639905fcad7056eaeb5 HEAD; then _patchname='proton-win10-default-e8b0c92.patch' && _patchmsg="Enforce win10 as default wine version" && nonuser_patcher elif git merge-base --is-ancestor 69154f0329aec4fb64886a0689da198b5323dcde HEAD; then diff --git a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default.patch b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default.patch index 8c6c0c321..2a6fd3e36 100644 --- a/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default.patch +++ b/wine-tkg-git/wine-tkg-patches/proton/proton-win10-default/proton-win10-default.patch @@ -81,56 +81,6 @@ For DeathLoop. programs/wineboot/wineboot.c | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) -diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc -index b6002f51f7a..c58acb8a8b6 100644 ---- a/dlls/kernel32/version.rc -+++ b/dlls/kernel32/version.rc -@@ -26,9 +26,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT - #define WINE_FILENAME_STR "kernel32.dll" - - /* these values come from Windows 10 Version 1909 */ --#define WINE_FILEVERSION 10,0,18362,1350 --#define WINE_FILEVERSION_STR "10.0.18362.1350" --#define WINE_PRODUCTVERSION 10,0,18362,1350 --#define WINE_PRODUCTVERSION_STR "10.0.18362.1350" -+#define WINE_FILEVERSION 10,0,18363,900 -+#define WINE_FILEVERSION_STR "10.0.18363.900" -+#define WINE_PRODUCTVERSION 10,0,18363,900 -+#define WINE_PRODUCTVERSION_STR "10.0.18363.900" - - #include "wine/wine_common_ver.rc" -diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c -index bd878cb324d..febb78d94e9 100644 ---- a/dlls/kernelbase/version.c -+++ b/dlls/kernelbase/version.c -@@ -151,7 +151,7 @@ static const struct - }, - /* Windows 10 */ - { -- { 10, 0, 18362 }, -+ { 10, 0, 18363 }, - {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} - } - }; -diff --git a/loader/wine.inf.in b/loader/wine.inf.in -index 4853db16e77..faba4be0b91 100644 ---- a/loader/wine.inf.in -+++ b/loader/wine.inf.in -@@ -2448,11 +2448,11 @@ HKLM,"System\CurrentControlSet\Services\Winsock\Parameters",,16 - HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Catalog_Entries",,16 - - [VersionInfo] --HKLM,%CurrentVersionNT%,"CurrentVersion",2,"6.3" -+HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" - HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10001,10 - HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10001,0 --HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18362" --HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18362" -+HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" -+HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" - HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" - HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ - 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index f78d4bdae25..eb69a5941a9 100644 --- a/programs/wineboot/wineboot.c @@ -180,67 +130,10 @@ Subject: [PATCH] Bump current build number to 19043 (Win10 2009). programs/wineboot/wineboot.c | 2 +- 6 files changed, 18 insertions(+), 15 deletions(-) -diff --git a/dlls/kernel32/version.rc b/dlls/kernel32/version.rc -index c58acb8a8b6..f91262dfa8e 100644 ---- a/dlls/kernel32/version.rc -+++ b/dlls/kernel32/version.rc -@@ -25,10 +25,10 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT - #define WINE_FILEDESCRIPTION_STR "Wine kernel DLL" - #define WINE_FILENAME_STR "kernel32.dll" - --/* these values come from Windows 10 Version 1909 */ --#define WINE_FILEVERSION 10,0,18363,900 --#define WINE_FILEVERSION_STR "10.0.18363.900" --#define WINE_PRODUCTVERSION 10,0,18363,900 --#define WINE_PRODUCTVERSION_STR "10.0.18363.900" -+/* these values come from Windows 10 Version 2009 */ -+#define WINE_FILEVERSION 10,0,19043,1466 -+#define WINE_FILEVERSION_STR "10.0.19043.1466" -+#define WINE_PRODUCTVERSION 10,0,19043,1466 -+#define WINE_PRODUCTVERSION_STR "10.0.19043.1466" - - #include "wine/wine_common_ver.rc" -diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c -index b4f9406f3c6..ab4ec32a260 100644 ---- a/dlls/kernelbase/version.c -+++ b/dlls/kernelbase/version.c -@@ -158,7 +158,7 @@ static const struct - }, - /* Windows 10 */ - { -- { 10, 0, 18363 }, -+ { 10, 0, 19043 }, - {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} - } - }; -diff --git a/dlls/ntdll/version.c b/dlls/ntdll/version.c -index 077ab65a502..6c846d0ac33 100644 ---- a/dlls/ntdll/version.c -+++ b/dlls/ntdll/version.c -@@ -167,7 +167,7 @@ static const RTL_OSVERSIONINFOEXW VersionData[NB_WINDOWS_VERSIONS] = - }, - /* WIN10 */ - { -- sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 18362, VER_PLATFORM_WIN32_NT, -+ sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 19043, VER_PLATFORM_WIN32_NT, - L"", 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 - }, - diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 769ff984187..0a41810d884 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in -@@ -2456,8 +2456,8 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca - HKLM,%CurrentVersionNT%,"CurrentVersion",2,"10.0" - HKLM,%CurrentVersionNT%,"CurrentMajorVersionNumber",0x10001,10 - HKLM,%CurrentVersionNT%,"CurrentMinorVersionNumber",0x10001,0 --HKLM,%CurrentVersionNT%,"CurrentBuild",2,"18363" --HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"18363" -+HKLM,%CurrentVersionNT%,"CurrentBuild",2,"19043" -+HKLM,%CurrentVersionNT%,"CurrentBuildNumber",2,"19043" - HKLM,%CurrentVersionNT%,"CurrentType",2,"Multiprocessor Free" - HKLM,%CurrentVersionNT%,"DigitalProductId",1,00,00,00,00,00,00,00,00,00,00,00,\ - 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ @@ -2466,8 +2466,9 @@ HKLM,"System\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ @@ -253,19 +146,6 @@ index 769ff984187..0a41810d884 100644 HKLM,%Control%\ProductOptions,"ProductType",2,"WinNT" HKLM,%Control%\Windows,"CSDVersion",0x10003,0 HKLM,%Control%\Session Manager\Environment,"OS",2,"Windows_NT" -diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c -index 14d12883813..5d1190583b1 100644 ---- a/programs/wineboot/wineboot.c -+++ b/programs/wineboot/wineboot.c -@@ -1415,7 +1415,7 @@ static void update_user_profile(void) - - static void update_win_version(void) - { -- static const WCHAR win10_buildW[] = L"18363"; -+ static const WCHAR win10_buildW[] = L"19043"; - - HKEY cv_h; - DWORD type, sz; From fc3d95a311a8e329af3d55a88fc68b41869af863 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 22 Dec 2021 00:57:25 +0300 diff --git a/wine-tkg-git/wine-tkg-patches/reverts b/wine-tkg-git/wine-tkg-patches/reverts index 7ad282bab..c7d42d3fb 100644 --- a/wine-tkg-git/wine-tkg-patches/reverts +++ b/wine-tkg-git/wine-tkg-patches/reverts @@ -98,3 +98,9 @@ _committorevert=461b5e56f95eb095d97e4af1cb1c5fd64bb2862a && nonuser_reverter echo -e "( Kernelbase reverts clean reverts applied )\n" >> "$_where"/last_build_config.log fi + + # GetPackagesByPackageFamily(), PackageFullNameFromId(), GetPackagePath() implemented through protonify + if [ "$_protonify" = "true" ]; then + _committorevert=240556e2b8cb94fc9cc85949b7e043f392b1802a && nonuser_reverter # GetPackagePathByFullName + _committorevert=bd89ab3040e30c11b34a95072d88f635ade03bdc && nonuser_reverter + fi diff --git a/wine-tkg-git/wine-tkg-patches/staging_fixes b/wine-tkg-git/wine-tkg-patches/staging_fixes index f058c48bc..c5a92c9f8 100644 --- a/wine-tkg-git/wine-tkg-patches/staging_fixes +++ b/wine-tkg-git/wine-tkg-patches/staging_fixes @@ -128,7 +128,7 @@ # Proton Bcrypt patches if [ "$_use_staging" = "true" ]; then - if [ "$_proton_bcrypt" = "true" ] && ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor 37e000145f07c4ec6f48fdac5969bbbb05435d52 HEAD ) && ! grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition"; then + if [ "$_NOLIB32" != "true" ] && [ "$_proton_bcrypt" = "true" ] && ( cd "${srcdir}"/"${_stgsrcdir}" && git merge-base --is-ancestor 37e000145f07c4ec6f48fdac5969bbbb05435d52 HEAD ) && ( [ -e "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ] && ! grep -Fxq 'Disabled: true' "${srcdir}/${_stgsrcdir}/patches/bcrypt-ECDHSecretAgreement/definition" ) && ! grep -Fxq 'Disabled: True' "${srcdir}/${_stgsrcdir}/patches/ntdll-Syscall_Emulation/definition"; then if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 74c0da2d71e95f3e6bd6c8b440652933771b27d7 HEAD );then _staging_args+=(-W bcrypt-ECDHSecretAgreement) fi diff --git a/wine-tkg-git/wine-tkg-profiles/chaotic-staging.cfg b/wine-tkg-git/wine-tkg-profiles/chaotic-staging.cfg index fede09bf8..b17ed5be0 100644 --- a/wine-tkg-git/wine-tkg-profiles/chaotic-staging.cfg +++ b/wine-tkg-git/wine-tkg-profiles/chaotic-staging.cfg @@ -6,6 +6,6 @@ _proton_fs_hack="true" _FS_bypass_compositor="true" _win10_default="true" _protonify="true" -_community_patches="amdags.mypatch ntdll_Map_top-down_if_dll_characteristics_include_DYNAMIC_BASE.mypatch mfplat_nv12_d3d11_buffers.mypatch" +_community_patches="amdags.mypatch" _user_patches_no_confirm="true" _hotfixes_no_confirm="true" diff --git a/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-70.cfg b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-70.cfg new file mode 100644 index 000000000..f89e599f5 --- /dev/null +++ b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-70.cfg @@ -0,0 +1,16 @@ +# 'Wine-to-rule-them-all' - Wine-TkG config file +# Proton 7.0 + +_PKGNAME_OVERRIDE="valve" + +_custom_wine_source="https://github.com/ValveSoftware/wine.git" +_plain_version="proton_7.0" +_staging_version="2fc92f8ba6e577b8baf69053aabe1c302f352197" + +_CSMT_toggle="false" +_GLSL_toggle="false" +_lol920_fix="false" + +_NOINITIALPROMPT="true" +_user_patches="true" +_user_patches_no_confirm="true" diff --git a/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp-70.cfg b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp-70.cfg new file mode 100644 index 000000000..8ceb51ff7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp-70.cfg @@ -0,0 +1,17 @@ +# 'Wine-to-rule-them-all' - Wine-TkG config file +# Proton experimental 7.0 + +_PKGNAME_OVERRIDE="valve-exp" + +_custom_wine_source="https://github.com/ValveSoftware/wine.git" +_plain_version="experimental_7.0" +_proton_branch="experimental_7.0" +_staging_version="2fc92f8ba6e577b8baf69053aabe1c302f352197" + +_CSMT_toggle="false" +_GLSL_toggle="false" +_lol920_fix="false" + +_NOINITIALPROMPT="true" +_user_patches="true" +_user_patches_no_confirm="true" diff --git a/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp.cfg b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp.cfg index d5c93833b..0edacd3cc 100644 --- a/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp.cfg +++ b/wine-tkg-git/wine-tkg-profiles/legacy/wine-tkg-valve-exp.cfg @@ -4,9 +4,9 @@ _PKGNAME_OVERRIDE="valve-exp" _custom_wine_source="https://github.com/ValveSoftware/wine.git" -_plain_version="experimental_7.0" -_proton_branch="experimental_7.0" -_staging_version="2fc92f8ba6e577b8baf69053aabe1c302f352197" +_plain_version="experimental_8.0" +_proton_branch="experimental_8.0" +_staging_version="e9f69afcab7a6143abb54854945bdbf7f091c60c" _CSMT_toggle="false" _GLSL_toggle="false" diff --git a/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg b/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg index 6fa6f6e3c..29c82b440 100644 --- a/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg +++ b/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg @@ -74,7 +74,9 @@ _generate_patchsets="false" # Set to "true" to log compiler warnings and errors to a debug.log file _log_errors_to_file="false" -# Set to true to disable usage of 32-bit unix libs and use Wine 8.0+ experimental WoW64 for 32-bit apps. Default is "false" as it's still under development. +# Set to "true" to disable usage of 32-bit unix libs and disable 32-bit apps support. +# Set to "wow64" to use Wine 8.0+ experimental WoW64 (32on64) for 32-bit apps. +# Default is "false". _NOLIB32="false" # Set to true to disable 64-bit wine. Doing so will only build 32-bit wine and no SUPPORT for 64-bit apps. Default is "false". diff --git a/wine-tkg-git/wine-tkg-profiles/wine-tkg-mainline.cfg b/wine-tkg-git/wine-tkg-profiles/wine-tkg-mainline.cfg index f3ab1fd73..d74036cfb 100644 --- a/wine-tkg-git/wine-tkg-profiles/wine-tkg-mainline.cfg +++ b/wine-tkg-git/wine-tkg-profiles/wine-tkg-mainline.cfg @@ -79,4 +79,6 @@ _win10_default="false" _protonify="false" _use_josh_flat_theme="false" _proton_bcrypt="false" +_proton_eac_support="false" +_proton_battleye_support="false" diff --git a/wine-tkg-git/wine-tkg-profiles/wine-tkg-protonified.cfg b/wine-tkg-git/wine-tkg-profiles/wine-tkg-protonified.cfg index 8ea8df5ac..42d63bfca 100644 --- a/wine-tkg-git/wine-tkg-profiles/wine-tkg-protonified.cfg +++ b/wine-tkg-git/wine-tkg-profiles/wine-tkg-protonified.cfg @@ -10,3 +10,4 @@ _PKGNAME_OVERRIDE="protonified" _FS_bypass_compositor="true" _proton_fs_hack="true" +_protonify="true" diff --git a/wine-tkg-git/wine-tkg-profiles/wine-tkg-staging.cfg b/wine-tkg-git/wine-tkg-profiles/wine-tkg-staging.cfg index 34f3c63a4..7f1ab5402 100644 --- a/wine-tkg-git/wine-tkg-profiles/wine-tkg-staging.cfg +++ b/wine-tkg-git/wine-tkg-profiles/wine-tkg-staging.cfg @@ -79,4 +79,5 @@ _win10_default="false" _protonify="false" _use_josh_flat_theme="false" _proton_bcrypt="false" - +_proton_eac_support="false" +_proton_battleye_support="false" diff --git a/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve-exp-bleeding.cfg b/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve-exp-bleeding.cfg index 4524ce087..d45a59bb9 100644 --- a/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve-exp-bleeding.cfg +++ b/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve-exp-bleeding.cfg @@ -4,9 +4,9 @@ _PKGNAME_OVERRIDE="valve-exp-bleeding" _custom_wine_source="https://github.com/ValveSoftware/wine.git" -_plain_version="experimental_7.0" -_proton_branch="experimental_7.0" -_staging_version="2fc92f8ba6e577b8baf69053aabe1c302f352197" +_plain_version="experimental_8.0" +_proton_branch="experimental_8.0" +_staging_version="e9f69afcab7a6143abb54854945bdbf7f091c60c" # Optionally set a bleeding edge tag - Leave empty to use latest bleeding edge _bleeding_tag="" diff --git a/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve.cfg b/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve.cfg index bff3eca55..6e9decf03 100644 --- a/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve.cfg +++ b/wine-tkg-git/wine-tkg-profiles/wine-tkg-valve.cfg @@ -4,8 +4,8 @@ _PKGNAME_OVERRIDE="valve" _custom_wine_source="https://github.com/ValveSoftware/wine.git" -_plain_version="proton_7.0" -_staging_version="2fc92f8ba6e577b8baf69053aabe1c302f352197" +_plain_version="proton_8.0" +_staging_version="e9f69afcab7a6143abb54854945bdbf7f091c60c" _CSMT_toggle="false" _GLSL_toggle="false" diff --git a/wine-tkg-git/wine-tkg-scripts/build-32.sh b/wine-tkg-git/wine-tkg-scripts/build-32.sh index 0ccb40fef..08950962d 100644 --- a/wine-tkg-git/wine-tkg-scripts/build-32.sh +++ b/wine-tkg-git/wine-tkg-scripts/build-32.sh @@ -6,18 +6,32 @@ _exports_32() { fi if [ -e /usr/bin/ccache ] && [ "$_NOMINGW" != "true" ]; then export CROSSCC="ccache i686-w64-mingw32-gcc" && echo "CROSSCC32 = ${CROSSCC}" >>"$_LAST_BUILD_CONFIG" + export i386_CC="${CROSSCC}" fi fi # build wine 32-bit if [ -d '/usr/lib32/pkgconfig' ]; then # Typical Arch path export PKG_CONFIG_PATH='/usr/lib32/pkgconfig' + # glib/gstreamer detection workaround for proton 8.0 trees + if [[ "$_plain_version" = *_8.0 ]]; then + CFLAGS+=" -I/usr/lib32/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib32/gstreamer-1.0/include" + CROSSCFLAGS+=" -I/usr/lib32/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib32/gstreamer-1.0/include" + fi elif [ -d '/usr/lib/i386-linux-gnu/pkgconfig' ]; then # Ubuntu 18.04/19.04 path export PKG_CONFIG_PATH='/usr/lib/i386-linux-gnu/pkgconfig' + if [[ "$_plain_version" = *_8.0 ]]; then + CFLAGS+=" -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib/i386-linux-gnu/gstreamer-1.0/include" + CROSSCFLAGS+=" -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib/i386-linux-gnu/gstreamer-1.0/include" + fi else export PKG_CONFIG_PATH='/usr/lib/pkgconfig' # Pretty common path, possibly helpful for OpenSuse & Fedora # Workaround for Fedora freetype2 libs not being detected now that it's been moved to a subdir CFLAGS+=" -I/usr/include/freetype2 -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/sysprof-4 -pthread" CROSSCFLAGS+=" -I/usr/include/freetype2" + if [[ "$_plain_version" = *_8.0 ]]; then + CFLAGS+=" -I/usr/lib/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib/gstreamer-1.0/include" + CROSSCFLAGS+=" -I/usr/lib/glib-2.0/include -I/usr/include/glib-2.0 -I/usr/include/gstreamer-1.0 -I/usr/lib/gstreamer-1.0/include" + fi fi } diff --git a/wine-tkg-git/wine-tkg-scripts/build-64.sh b/wine-tkg-git/wine-tkg-scripts/build-64.sh index bd098a37d..deefae89d 100644 --- a/wine-tkg-git/wine-tkg-scripts/build-64.sh +++ b/wine-tkg-git/wine-tkg-scripts/build-64.sh @@ -6,6 +6,12 @@ _exports_64() { fi if [ -e /usr/bin/ccache ] && [ "$_NOMINGW" != "true" ]; then export CROSSCC="ccache x86_64-w64-mingw32-gcc" && echo "CROSSCC64 = ${CROSSCC}" >>"$_LAST_BUILD_CONFIG" + export x86_64_CC="${CROSSCC}" + + # Required for new-style WoW64 builds (otherwise 32-bit portions won't be ccached) + if [ "${_NOLIB32}" != "false" ]; then + export i386_CC="ccache i686-w64-mingw32-gcc" + fi fi fi # If /usr/lib32 doesn't exist (such as on Fedora), make sure we're using /usr/lib64 for 64-bit pkgconfig path @@ -19,7 +25,7 @@ _configure_64() { cd "${srcdir}"/"${pkgname}"-64-build if [ "$_NUKR" != "debug" ] || [[ "$_DEBUGANSW3" =~ [yY] ]]; then chmod +x ../"${_winesrcdir}"/configure - if [ "$_NOLIB32" != "true" ]; then + if [ "$_NOLIB32" != "wow64" ]; then ../"${_winesrcdir}"/configure \ --prefix="$_prefix" \ --enable-win64 \ diff --git a/wine-tkg-git/wine-tkg-scripts/build.sh b/wine-tkg-git/wine-tkg-scripts/build.sh index 19787324d..664512ab9 100644 --- a/wine-tkg-git/wine-tkg-scripts/build.sh +++ b/wine-tkg-git/wine-tkg-scripts/build.sh @@ -125,7 +125,7 @@ _build_serial() { _configure_64 _build_64 fi - if [ "$_NOLIB32" != "true" ]; then + if [ "$_NOLIB32" != "true" ] && [ "$_NOLIB32" != "wow64" ]; then # build wine 32-bit # nomakepkg if [ "$_nomakepkg_midbuild_prompt" = "true" ]; then @@ -209,7 +209,7 @@ _package_nomakepkg() { fi fi - if [ "$_NOLIB32" != "true" ]; then + if [ "$_NOLIB32" = "false" ]; then # package wine 32-bit # (according to the wine wiki, this reverse 32-bit/64-bit packaging order is important) msg2 'Packaging Wine-32...' @@ -238,6 +238,12 @@ _package_nomakepkg() { cp -v "$_where"/wine-tkg-scripts/wine64-tkg "$_prefix"/bin/wine64-tkg cp -v "$_where"/wine-tkg-scripts/wine-tkg-interactive "$_prefix"/bin/wine-tkg-interactive + # Fixes compatibility with installation scripts (like winetricks) that use + # the wine64 binary, which is not present in WoW64 builds. + if [ "$_NOLIB32" = "wow64" ]; then + ( cd "$_prefix/bin" && ln -s wine wine64 ) + fi + # strip if [ "$_EXTERNAL_INSTALL" != "proton" ]; then if [ "$_protonify" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor 2e5e5ade82b5e3b1d70ebe6b1a824bdfdedfd04e HEAD ); then @@ -351,7 +357,7 @@ _package_makepkg() { fi fi - if [ "$_NOLIB32" != "true" ]; then + if [ "$_NOLIB32" = "false" ]; then # package wine 32-bit # (according to the wine wiki, this reverse 32-bit/64-bit packaging order is important) msg2 'Packaging Wine-32...' @@ -404,6 +410,12 @@ _package_makepkg() { cp "$_where"/wine-tkg-scripts/wine64-tkg "${pkgdir}$_prefix"/bin/wine64-tkg cp "$_where"/wine-tkg-scripts/wine-tkg-interactive "${pkgdir}$_prefix"/bin/wine-tkg-interactive + # Fixes compatibility with installation scripts (like winetricks) that use + # the wine64 binary, which is not present in WoW64 builds. + if [ "$_NOLIB32" = "wow64" ]; then + ( cd "${pkgdir}$_prefix/bin" && ln -s wine wine64 ) + fi + # strip if [ "$_EXTERNAL_INSTALL" != "proton" ]; then if [ "$_protonify" = "true" ] && ( cd "${srcdir}"/"${_winesrcdir}" && ! git merge-base --is-ancestor 2e5e5ade82b5e3b1d70ebe6b1a824bdfdedfd04e HEAD ); then diff --git a/wine-tkg-git/wine-tkg-scripts/deps b/wine-tkg-git/wine-tkg-scripts/deps index 7d421d15f..5045c324e 100755 --- a/wine-tkg-git/wine-tkg-scripts/deps +++ b/wine-tkg-git/wine-tkg-scripts/deps @@ -6,7 +6,7 @@ _debuntu_64() { # 64-bit msg2 "" msg2 "Installing 64-bit dependencies for Debian-based distros..." - ${_as_root}apt install git libunwind-dev autoconf bison ccache debhelper desktop-file-utils docbook-to-man docbook-utils docbook-xsl flex fontforge gawk gettext libacl1-dev libasound2-dev libcapi20-dev libcups2-dev libdbus-1-dev libgif-dev libglu1-mesa-dev libgphoto2-dev libgsm1-dev libgtk-3-dev libkrb5-dev libxi-dev liblcms2-dev libldap2-dev libmpg123-dev libncurses5-dev libopenal-dev libosmesa6-dev libpcap-dev libpulse-dev libsane-dev libssl-dev libtiff5-dev libudev-dev libv4l-dev libva-dev libxslt1-dev libxt-dev ocl-icd-opencl-dev oss4-dev prelink sharutils unixodbc-dev valgrind schedtool libfreetype6-dev xserver-xorg-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gcc-multilib g++-multilib curl fonttools libsdl2-dev python3-tk libvulkan1 libc6-dev linux-libc-dev libkdb5-* libppl14 libcolord2 libvulkan-dev libgnutls28-dev libpng-dev libkadm5clnt-mit* libkadm5srv-mit* libavcodec-dev libavutil-dev libswresample-dev libavcodec58 libswresample3 libavutil56 libfaudio0 libfaudio-dev libvkd3d-dev libxinerama-dev libxcursor-dev libxrandr-dev libxcomposite-dev mingw-w64 glslang-dev glslang-tools meson wget python3-pefile rustc cargo python3-ldb samba-libs samba-dev libgcrypt20-dev libusb-1.0-0-dev yasm + ${_as_root}apt install git libunwind-dev autoconf bison ccache debhelper desktop-file-utils docbook-to-man docbook-utils docbook-xsl flex fontforge gawk gettext libacl1-dev libasound2-dev libcapi20-dev libcups2-dev libdbus-1-dev libgif-dev libglu1-mesa-dev libgphoto2-dev libgsm1-dev libgtk-3-dev libkrb5-dev libxi-dev liblcms2-dev libldap2-dev libmpg123-dev libncurses5-dev libopenal-dev libosmesa6-dev libpcap-dev libpulse-dev libsane-dev libssl-dev libtiff5-dev libudev-dev libv4l-dev libva-dev libxslt1-dev libxt-dev ocl-icd-opencl-dev oss4-dev prelink sharutils unixodbc-dev valgrind schedtool libfreetype6-dev xserver-xorg-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gcc-multilib g++-multilib curl fonttools libsdl2-dev python3-tk libvulkan1 libc6-dev linux-libc-dev libkdb5-* libppl14 libcolord2 libvulkan-dev libgnutls28-dev libpng-dev libkadm5clnt-mit* libkadm5srv-mit* libavcodec-dev libavutil-dev libswresample-dev libavcodec58 libswresample3 libavutil56 libfaudio0 libfaudio-dev libvkd3d-dev libxinerama-dev libxcursor-dev libxrandr-dev libxcomposite-dev mingw-w64 glslang-dev glslang-tools meson wget python3-pefile rustc cargo python3-ldb samba-libs samba-dev libgcrypt20-dev libusb-1.0-0-dev yasm jq ${_as_root}update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix || ${_as_root}update-alternatives --config x86_64-w64-mingw32-gcc ${_as_root}update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix || ${_as_root}update-alternatives --config x86_64-w64-mingw32-g++ ${_as_root}update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix || ${_as_root}update-alternatives --config i686-w64-mingw32-gcc @@ -28,8 +28,8 @@ _fedora_64() { # 64-bit msg2 "" msg2 "Installing 64-bit dependencies for Fedora based distros..." - ${_as_root}dnf install git make ccache gcc-c++ mingw32-gcc mingw64-gcc SDL2-devel openal-soft-devel opencl-headers ocl-icd-devel libFAudio-devel libvkd3d-devel icoutils vulkan-devel lcms2-devel gstreamer1-devel gstreamer1-plugins-base-devel libmpg123-devel gtk3-devel libva-devel fontforge fontpackages-devel gsm-devel libjpeg-turbo-devel libudev-devel libv4l-devel pulseaudio-libs-devel lzma audiofile-devel giflib-devel ImageMagick-devel libpcap-devel libXxf86dga-devel mesa-libOSMesa-devel libgphoto2-devel alsa-lib-devel autoconf bison coreutils cups-devel dbus-devel desktop-file-utils flex fontconfig-devel freetype-devel freeglut-devel gawk xz gettext-devel gnutls-devel krb5-devel libattr-devel libieee1284-devel libpng-devel librsvg2 librsvg2-devel libstdc++-devel libtiff-devel libX11-devel libXcomposite-devel libXcursor-devel libXext-devel libXi-devel libXinerama-devel libxml2-devel libXmu-devel libXrandr-devel libXrender-devel libxslt-devel libXxf86vm-devel mesa-libGL-devel mesa-libGLU-devel ncurses-devel openldap-devel sane-backends-devel unixODBC-devel unzip util-linux zlib-devel fonttools wget python-pefile rust cargo glslang patch libpng-static.x86_64 libgcrypt-devel libXpresent-devel yasm - ${_as_root}dnf install libusb-devel + ${_as_root}dnf install git make ccache gcc-c++ mingw32-gcc mingw64-gcc SDL2-devel openal-soft-devel opencl-headers ocl-icd-devel libFAudio-devel libvkd3d-devel icoutils vulkan-devel lcms2-devel gstreamer1-devel gstreamer1-plugins-base-devel libmpg123-devel gtk3-devel libva-devel fontforge fontpackages-devel gsm-devel libjpeg-turbo-devel libudev-devel libv4l-devel pulseaudio-libs-devel lzma audiofile-devel giflib-devel ImageMagick-devel libpcap-devel libXxf86dga-devel mesa-libOSMesa-devel libgphoto2-devel alsa-lib-devel autoconf bison coreutils cups-devel dbus-devel desktop-file-utils flex fontconfig-devel freetype-devel freeglut-devel gawk xz gettext-devel gnutls-devel krb5-devel libattr-devel libieee1284-devel libpng-devel librsvg2 librsvg2-devel libstdc++-devel libtiff-devel libX11-devel libXcomposite-devel libXcursor-devel libXext-devel libXi-devel libXinerama-devel libxml2-devel libXmu-devel libXrandr-devel libXrender-devel libxslt-devel libXxf86vm-devel mesa-libGL-devel mesa-libGLU-devel ncurses-devel openldap-devel sane-backends-devel unixODBC-devel unzip util-linux zlib-devel fonttools wget python-pefile rust cargo glslang patch libpng-static.x86_64 libgcrypt-devel libXpresent-devel yasm jq + ${_as_root}dnf install libusb1-devel.x86_64 libusbg-devel.x86_64 } _fedora_32() { @@ -38,9 +38,8 @@ _fedora_32() { # 32-bit msg2 "" msg2 "Installing 32-bit dependencies for Fedora based distros..." - ${_as_root}dnf install pkgconf.i686 gcc-c++.i686 glibc-devel.i686 libX11-devel.i686 libXcomposite-devel.i686 libXcursor-devel.i686 libXext-devel.i686 libXi-devel.i686 libXinerama-devel.i686 libxml2-devel.i686 libXmu-devel.i686 libXrandr-devel.i686 libXrender-devel.i686 libxslt-devel.i686 libXxf86vm-devel.i686 mesa-libGL-devel.i686 mesa-libGLU-devel.i686 ncurses-devel.i686 openldap-devel.i686 freetype-devel.i686 SDL2-devel.i686 openal-soft-devel.i686 ocl-icd-devel.i686 libFAudio-devel.i686 libvkd3d-devel.i686 lcms2-devel.i686 gstreamer1-devel.i686 gstreamer1-plugins-base-devel.i686 gtk3-devel.i686 libva-devel.i686 giflib-devel.i686 libXxf86dga-devel.i686 mesa-libOSMesa-devel.i686 libgphoto2-devel.i686 alsa-lib-devel.i686 cups-devel.i686 dbus-devel.i686 fontconfig-devel.i686 libpng-devel.i686 libjpeg-turbo-devel.i686 pulseaudio-libs-devel.i686 gnutls-devel.i686 krb5-devel.i686 krb5-libs.i686 libstdc++-devel.i686 libtiff-devel.i686 vulkan-loader-devel.i686 libv4l-devel.i686 gsm-devel.i686 sane-backends-devel.i686 libXfixes-devel.i686 libpng-static.i686 rust-std-static.i686 libgcrypt-devel.i686 libXpresent-devel.i686 - ${_as_root}dnf install libusb-devel.i686 - ${_as_root}dnf install libpcap-devel.i686 + ${_as_root}dnf install pkgconf.i686 gcc-c++.i686 glibc-devel.i686 libX11-devel.i686 libXcomposite-devel.i686 libXcursor-devel.i686 libXext-devel.i686 libXi-devel.i686 libXinerama-devel.i686 libxml2-devel.i686 libXmu-devel.i686 libXrandr-devel.i686 libXrender-devel.i686 libxslt-devel.i686 libXxf86vm-devel.i686 mesa-libGL-devel.i686 mesa-libGLU-devel.i686 ncurses-devel.i686 openldap-devel.i686 freetype-devel.i686 SDL2-devel.i686 openal-soft-devel.i686 ocl-icd-devel.i686 libFAudio-devel.i686 libvkd3d-devel.i686 lcms2-devel.i686 gstreamer1-devel.i686 gstreamer1-plugins-base-devel.i686 gtk3-devel.i686 libva-devel.i686 giflib-devel.i686 libpcap-devel.i686 libXxf86dga-devel.i686 mesa-libOSMesa-devel.i686 libgphoto2-devel.i686 alsa-lib-devel.i686 cups-devel.i686 dbus-devel.i686 fontconfig-devel.i686 libpng-devel.i686 libjpeg-turbo-devel.i686 pulseaudio-libs-devel.i686 gnutls-devel.i686 krb5-devel.i686 krb5-libs.i686 libstdc++-devel.i686 vulkan-loader-devel.i686 libv4l-devel.i686 gsm-devel.i686 sane-backends-devel.i686 libXfixes-devel.i686 libpng-static.i686 rust-std-static.i686 libgcrypt-devel.i686 libXpresent-devel.i686 + ${_as_root}dnf install libusb1-devel.i686 libusbg-devel.i686 } _archlinux_64() { @@ -49,7 +48,7 @@ _archlinux_64() { # 64-bit msg2 "" msg2 "Installing 64-bit dependencies for Archlinux based distros..." - ${_as_root}pacman -S --needed attr fontconfig lcms2 libxml2 libxcursor libxrandr libxdamage libxi gettext freetype2 glu libsm gcc-libs libpcap faudio desktop-file-utils git autoconf ncurses bison perl fontforge flex gcc pkgconf giflib libpng gnutls libxinerama libxcomposite libxmu libxxf86vm libldap mpg123 openal v4l-utils alsa-lib mesa libgl libxslt libpulse libva gtk3 gst-plugins-base-libs gst-plugins-good vulkan-headers vulkan-icd-loader sdl2 libcups samba opencl-headers meson ninja glslang wget ocl-icd giflib libpng alsa-plugins libjpeg-turbo cups dosbox ccache schedtool mingw-w64-gcc python-fonttools python-pefile rust gst-plugins-ugly libxpresent libgcrypt yasm + ${_as_root}pacman -S --needed attr fontconfig lcms2 libxml2 libxcursor libxrandr libxdamage libxi gettext freetype2 glu libsm gcc-libs libpcap faudio desktop-file-utils git autoconf ncurses bison perl fontforge flex gcc pkgconf giflib libpng gnutls libxinerama libxcomposite libxmu libxxf86vm libldap mpg123 openal v4l-utils alsa-lib mesa libgl libxslt libpulse libva gtk3 gst-plugins-base-libs gst-plugins-good vulkan-headers vulkan-icd-loader sdl2 libcups samba opencl-headers meson ninja glslang wget ocl-icd giflib libpng alsa-plugins libjpeg-turbo cups dosbox ccache schedtool mingw-w64-gcc python-fonttools python-pefile rust gst-plugins-ugly libxpresent libgcrypt yasm jq } _archlinux_32() { diff --git a/wine-tkg-git/wine-tkg-scripts/prepare.sh b/wine-tkg-git/wine-tkg-scripts/prepare.sh index 049d9214c..0522d6a24 100644 --- a/wine-tkg-git/wine-tkg-scripts/prepare.sh +++ b/wine-tkg-git/wine-tkg-scripts/prepare.sh @@ -282,6 +282,12 @@ msg2 '' if [[ "$_LOCAL_PRESET" = valve* ]]; then _NOLIB32="false" _NOLIB64="false" + else + if [ "$_NOLIB32" = "true" ]; then + warning '_NOLIB32="true" is not compatible with Proton builds and was set to "false" as a fallback' + _NOLIB32="false" + fi + _NOLIB64="false" fi _esync_version="" _use_faudio="true" @@ -980,7 +986,7 @@ _prepare() { source "$_where"/wine-tkg-patches/proton/valve_proton_fullscreen_hack/valve_proton_fullscreen_hack source "$_where"/wine-tkg-patches/misc/childwindow/childwindow-proton - source "$_where"/wine-tkg-patches/proton/shared-gpu-resources/shared-gpu-resources + #source "$_where"/wine-tkg-patches/proton/shared-gpu-resources/shared-gpu-resources # broken patchset on any version source "$_where"/wine-tkg-patches/proton/proton-rawinput/proton-rawinput source "$_where"/wine-tkg-patches/misc/winevulkan/winevulkan source "$_where"/wine-tkg-patches/game-specific/overwatch-mfstub/overwatch-mfstub @@ -1081,6 +1087,9 @@ _polish() { echo -e "\nRunning make_vulkan" >> "$_where"/prepare.log && dlls/winevulkan/make_vulkan >> "$_where"/prepare.log 2>&1 tools/make_requests + if [ -e tools/make_specfiles ]; then + tools/make_specfiles + fi autoreconf -fiv # wine late user patches - Applied after make_vulkan/make_requests/autoreconf diff --git a/wine-tkg-git/wine-tkg-scripts/wine-tkg b/wine-tkg-git/wine-tkg-scripts/wine-tkg index 0d42427d5..53d4afea8 100755 --- a/wine-tkg-git/wine-tkg-scripts/wine-tkg +++ b/wine-tkg-git/wine-tkg-scripts/wine-tkg @@ -3,15 +3,15 @@ winetkgbindir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" if [ -z "$PATH" ]; then - PATH="$winetkgbindir" + export PATH="$winetkgbindir" else - PATH="$winetkgbindir:$PATH" + export PATH="$winetkgbindir:$PATH" fi if [ -z "$LD_LIBRARY_PATH" ]; then - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" else - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" fi # Debian-based distros @@ -19,7 +19,7 @@ if [ -f "/etc/debian_version" ]; then export WINEDLLPATH="/usr/lib/x86_64-linux-gnu/wine:/usr/lib/i386-linux-gnu/wine" fi -cat << 'EOM' +cat >&2 << 'EOM' .---.` `.---. `/syhhhyso- -osyhhhys/` .syNMdhNNhss/``.---.``/sshNNhdMNys. @@ -42,7 +42,7 @@ cat << 'EOM' EOM if [ -x "$(command -v _wine)" ]; then - _wine ${@:1} + _wine "${@:1}" else - wine ${@:1} + wine "${@:1}" fi diff --git a/wine-tkg-git/wine-tkg-scripts/wine-tkg-interactive b/wine-tkg-git/wine-tkg-scripts/wine-tkg-interactive index 4024f3f4d..356c064a2 100755 --- a/wine-tkg-git/wine-tkg-scripts/wine-tkg-interactive +++ b/wine-tkg-git/wine-tkg-scripts/wine-tkg-interactive @@ -3,15 +3,15 @@ winetkgbindir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" if [ -z "$PATH" ]; then - PATH="$winetkgbindir" + export PATH="$winetkgbindir" else - PATH="$winetkgbindir:$PATH" + export PATH="$winetkgbindir:$PATH" fi if [ -z "$LD_LIBRARY_PATH" ]; then - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" else - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" fi # Debian-based distros @@ -19,7 +19,7 @@ if [ -f "/etc/debian_version" ]; then export WINEDLLPATH="/usr/lib/x86_64-linux-gnu/wine:/usr/lib/i386-linux-gnu/wine" fi -cat << 'EOM' +cat >&2 << 'EOM' .---.` `.---. `/syhhhyso- -osyhhhys/` .syNMdhNNhss/``.---.``/sshNNhdMNys. diff --git a/wine-tkg-git/wine-tkg-scripts/wine64-tkg b/wine-tkg-git/wine-tkg-scripts/wine64-tkg index 30c10d6c4..4a84eacb8 100755 --- a/wine-tkg-git/wine-tkg-scripts/wine64-tkg +++ b/wine-tkg-git/wine-tkg-scripts/wine64-tkg @@ -3,15 +3,15 @@ winetkgbindir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" if [ -z "$PATH" ]; then - PATH="$winetkgbindir" + export PATH="$winetkgbindir" else - PATH="$winetkgbindir:$PATH" + export PATH="$winetkgbindir:$PATH" fi if [ -z "$LD_LIBRARY_PATH" ]; then - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32" else - LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="$winetkgbindir/../lib64:$winetkgbindir/../lib:$winetkgbindir/../lib32:$LD_LIBRARY_PATH" fi # Debian-based distros @@ -19,7 +19,7 @@ if [ -f "/etc/debian_version" ]; then export WINEDLLPATH="/usr/lib/x86_64-linux-gnu/wine:/usr/lib/i386-linux-gnu/wine" fi -cat << 'EOM' +cat >&2 << 'EOM' .---.` `.---. `/syhhhyso- -osyhhhys/` .syNMdhNNhss/``.---.``/sshNNhdMNys. @@ -42,7 +42,7 @@ cat << 'EOM' EOM if [ -x "$(command -v _wine64)" ]; then - _wine64 ${@:1} + _wine64 "${@:1}" else - wine64 ${@:1} + wine64 "${@:1}" fi